-
Bug
-
Resolution: Fixed
-
High
-
2.8
-
None
While debugging an issue in our custom authenticator that creates and updates user accounts during logins, I found something that appears to be a bug in how DefaultHibernateUser objects are being handled (and cached?) in confluence.
It looks like the DefaultHibernateUser objects are being cached with its (groups) collections attached with the hibernate session that performed an operation on the object recently (in a different thread). When another thread with a new hibernate session attempts to update such a user instance the exception below is thrown. We are not passing the objects between threads via http session or in any other way. Each thread obtains an instance of the user class via userAccessor.
Steps to reproduce:
You need two threads that get the user instance via an instance of UserAccessor, and call UserAccessor#saveUser(confUser).
- thread 1 opens a hibernate session
- thread 2 opens a hibernate session
- thread 1 fetches a user
- thread 1 saves the user
- thread 2 fetches the same user as thread #1
- thread 2 saves the user
- the exception is thrown in thread 2
- thread 1 closes the hibernate session - this is important, it seems that if the session was closed before thread 2 called saveUser, everything would be ok
- thread 2 closes the hibernate session
The exception:
org.springframework.orm.hibernate.HibernateSystemException: Illegal attempt to associate a collection with two open sessions; nested exception is net.sf.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions Caused by: net.sf.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions at net.sf.hibernate.collection.PersistentCollection.setCurrentSession(PersistentCollection.java:257) at net.sf.hibernate.impl.OnUpdateVisitor.processCollection(OnUpdateVisitor.java:38) at net.sf.hibernate.impl.AbstractVisitor.processValue(AbstractVisitor.java:69) at net.sf.hibernate.impl.AbstractVisitor.processValues(AbstractVisitor.java:36) at net.sf.hibernate.impl.AbstractVisitor.process(AbstractVisitor.java:91) at net.sf.hibernate.impl.SessionImpl.doUpdateMutable(SessionImpl.java:1466) at net.sf.hibernate.impl.SessionImpl.doUpdate(SessionImpl.java:1480) at net.sf.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:1403) at org.springframework.orm.hibernate.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:604) at org.springframework.orm.hibernate.HibernateTemplate.execute(HibernateTemplate.java:370) at org.springframework.orm.hibernate.HibernateTemplate.saveOrUpdate(HibernateTemplate.java:601) at com.atlassian.user.impl.hibernate.HibernateUserManager.saveUser(HibernateUserManager.java:171) at com.atlassian.user.impl.cache.CachingUserManager.saveUser(CachingUserManager.java:143) at com.atlassian.user.impl.delegation.DelegatingUserManager.saveUser(DelegatingUserManager.java:144) at bucket.user.DefaultUserAccessor.saveUser(DefaultUserAccessor.java:224) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:296) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:177) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:144) at com.atlassian.spring.interceptors.SpringProfilingInterceptor.invoke(SpringProfilingInterceptor.java:20) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:166) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:107) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:166) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) at $Proxy22.saveUser(Unknown Source) at com.sun.dse.wikis.auth.ConfluenceUserManager$1.doInHibernate(ConfluenceUserManager.java:96) at org.springframework.orm.hibernate.HibernateTemplate.execute(HibernateTemplate.java:370) at org.springframework.orm.hibernate.HibernateTemplate.execute(HibernateTemplate.java:337) at com.sun.dse.wikis.auth.ConfluenceUserManager.updateUser(ConfluenceUserManager.java:79) at com.sun.dse.wikis.auth.SsoFilter.doFilter(SsoFilter.java:124) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:217) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:185) at com.atlassian.confluence.util.ClusterHeaderFilter.doFilter(ClusterHeaderFilter.java:35) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:217) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:185) at org.springframework.orm.hibernate.support.OpenSessionInV