Merge branch 'release/5.0.0'
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / conversation / ConversationHolder.java
index c11c80fef8dcfba83bf45916e71c6e5fc447ca7a..2fe7ae07f3ae1c0d6f24cd00c80d580c0d5782a7 100644 (file)
@@ -37,9 +37,11 @@ import eu.etaxonomy.cdm.persistence.hibernate.CdmPostDataChangeObservableListene
  * <p>
  * This behaviour essentially revolves around the resources map in the {@link org.springframework.transaction.support.TransactionSynchronizationManager TransactionSynchronizationManager}.
  * This resources map contains two entries of interest,
- *  - (Autowired) {@link org.hibernate.SessionFactory} mapped to the {@link org.springframework.orm.hibernate5.SessionHolder}
- *  - (Autowired) {@link javax.sql.DataSource} mapped to the {@link org.springframework.jdbc.datasource.ConnectionHolder}
- *  <p>
+ * <ul>
+ *  <li>(Autowired) {@link org.hibernate.SessionFactory} mapped to the {@link org.springframework.orm.hibernate5.SessionHolder}</li>
+ *  <li>(Autowired) {@link javax.sql.DataSource} mapped to the {@link org.springframework.jdbc.datasource.ConnectionHolder}</li>
+ * </ul>
+ * <p>
  * The SessionHolder object itself contains the {@link org.hibernate.Session Session} as well as the {@link org.hibernate.Transaction object.
  * The ConnectionHolder contains the (JDBC) {@link java.sql.Connection Connection} to the database. For every action to do with the
  * transaction object it is required to have both entries present in the resources. Both the session as well as the connection
@@ -51,11 +53,13 @@ import eu.etaxonomy.cdm.persistence.hibernate.CdmPostDataChangeObservableListene
  * is in fact how hibernate implements the default 'session-per-request' pattern internally.
  * <p>
  * Given the above conditions, this class manages long running sessions by providing the following methods,
- * - {@link #bind()} : binds the session owned by this conversation to the resource map
- * - {@link #startTransaction()} : starts a transaction
- * - {@link #commit()} : commits the current transaction, with the option of restarting a new transaction.
- * - {@link #unbind()} : unbinds the session owned by this conversation from the resource map.
- * - {@link #close()} : closes the session owned by this conversation
+ * <ul>
+ *  <li>{@link #bind()} : binds the session owned by this conversation to the resource map.</li>
+ *  <li>{@link #startTransaction()} : starts a transaction.</li>
+ *  <li>{@link #commit()} : commits the current transaction, with the option of restarting a new transaction.</li>
+ *  <li>{@link #unbind()} : unbinds the session owned by this conversation from the resource map.</li>
+ *  <li>{@link #close()} : closes the session owned by this conversation.</li>
+ * </ul>
  * <p>
  * With the exception of {@link #unbind()} (which should be called explicitly), the above sequence must be strictly followed to
  * maintain a consistent session state. Even though it is possible to interweave multiple conversations at the same time, for a
@@ -64,8 +68,7 @@ import eu.etaxonomy.cdm.persistence.hibernate.CdmPostDataChangeObservableListene
  * @see http://www.hibernate.org/42.html
  *
  * @author n.hoffmann,c.mathew
- * @created 12.03.2009
- * @version 1.0
+ * @since 12.03.2009
  */
 public class ConversationHolder {
 
@@ -104,6 +107,8 @@ public class ConversationHolder {
 
     private boolean closed = false;
 
+    private FlushMode defaultFlushMode = FlushMode.COMMIT;
+
     /**
      * Simple constructor used by Spring only
      */
@@ -111,17 +116,37 @@ public class ConversationHolder {
         closed = false;
     }
 
+    /**
+     * Create a new Conversation holder and bind it immediately.
+     *
+     * @param dataSource
+     * @param sessionFactory
+     * @param transactionManager
+     */
     public ConversationHolder(DataSource dataSource, SessionFactory sessionFactory,
             PlatformTransactionManager transactionManager) {
+        this(dataSource, sessionFactory, transactionManager, true);
+    }
+
+    /**
+     * Create a new Conversation holder and optionally bind it immediately.
+     *
+     * @param dataSource
+     * @param sessionFactory
+     * @param transactionManager
+     */
+    public ConversationHolder(DataSource dataSource, SessionFactory sessionFactory,
+            PlatformTransactionManager transactionManager, boolean bindNow) {
         this();
         this.dataSource = dataSource;
         this.sessionFactory = sessionFactory;
         this.transactionManager = transactionManager;
 
-        bind();
-
-        if(TransactionSynchronizationManager.hasResource(getDataSource())){
-            TransactionSynchronizationManager.unbindResource(getDataSource());
+        if(bindNow) {
+            bind();
+            if(TransactionSynchronizationManager.hasResource(getDataSource())){
+                TransactionSynchronizationManager.unbindResource(getDataSource());
+            }
         }
 
     }
@@ -135,6 +160,7 @@ public class ConversationHolder {
         logger.info("Binding resources for ConversationHolder");
 
         if(TransactionSynchronizationManager.isSynchronizationActive()){
+            logger.trace("Clearing active  transaction synchronization");
             TransactionSynchronizationManager.clearSynchronization();
         }
 
@@ -145,10 +171,15 @@ public class ConversationHolder {
 
 
             if(TransactionSynchronizationManager.hasResource(getSessionFactory())){
+                logger.trace("Unbinding resource from TransactionSynchronizationManager with key: " + getSessionFactory());
                 TransactionSynchronizationManager.unbindResource(getSessionFactory());
             }
 
-            logger.info("Binding Session to TransactionSynchronizationManager: Session: " + getSessionHolder());
+            if(logger.isTraceEnabled()){
+                logger.trace("Binding Session to TransactionSynchronizationManager:" + getSessionHolder() + " Session [" + getSessionHolder().getSession().hashCode() + "] with key: " + getSessionFactory());
+            } else {
+                logger.info("Binding Session to TransactionSynchronizationManager: Session: " + getSessionHolder());
+            }
             TransactionSynchronizationManager.bindResource(getSessionFactory(), getSessionHolder());
 
 
@@ -175,8 +206,14 @@ public class ConversationHolder {
             // unbind the current session.
             // there is no need to bind a new session, since HibernateTransactionManager will create a new one
             // if the resource map does not contain one (ditto for the datasource-to-connection entry).
+            if(logger.isTraceEnabled()){
+                logger.trace("Unbinding SessionFactory [" + getSessionFactory().hashCode() + "]");
+            }
             TransactionSynchronizationManager.unbindResource(getSessionFactory());
             if(TransactionSynchronizationManager.hasResource(getDataSource())){
+                if(logger.isTraceEnabled()){
+                    logger.trace("Unbinding DataSource [" + getDataSource().hashCode() + "]");
+                }
                 TransactionSynchronizationManager.unbindResource(getDataSource());
             }
         }
@@ -184,8 +221,8 @@ public class ConversationHolder {
 
     public SessionHolder getSessionHolder(){
         if(this.sessionHolder == null){
-            logger.info("Creating new SessionHolder");
             this.sessionHolder = new SessionHolder(getSession());
+            logger.info("Creating new SessionHolder:" + sessionHolder);
         }
         return this.sessionHolder;
     }
@@ -203,7 +240,7 @@ public class ConversationHolder {
     public boolean isBound(){
         //return sessionHolder != null && longSession != null && longSession.isConnected();
         SessionHolder currentSessionHolder = (SessionHolder)TransactionSynchronizationManager.getResource(getSessionFactory());
-        return longSession != null && currentSessionHolder != null && getSessionFactory().getCurrentSession() == longSession;
+        return longSession != null && currentSessionHolder != null && getSessionFactory().getCurrentSession().equals(longSession);
     }
 
     /**
@@ -235,26 +272,17 @@ public class ConversationHolder {
      * @return if there is a running transaction
      */
     public boolean isTransactionActive(){
-        return transactionStatus != null;
+        return transactionStatus != null && !transactionStatus.isCompleted();
     }
 
-    /* (non-Javadoc)
-     * @see org.hibernate.Session#evict(java.lang.Object object)
-     */
     public void evict(Object object){
         getSession().evict(object);
     }
 
-    /* (non-Javadoc)
-     * @see org.hibernate.Session#refresh(java.lang.Object object)
-     */
     public void refresh(Object object){
         getSession().refresh(object);
     }
 
-    /* (non-Javadoc)
-     * @see org.hibernate.Session#clear()
-     */
     public void clear(){
         getSession().clear();
     }
@@ -324,8 +352,19 @@ public class ConversationHolder {
      * @return the session associated with this conversation manager
      */
     public Session getSession() {
+
+        String whatStr;
+
         if(longSession == null){
             longSession = getNewSession();
+            whatStr = "Creating";
+        } else {
+            whatStr = "Reusing";
+        }
+        if(logger.isDebugEnabled()){
+            logger.debug(whatStr + " Session: [" + longSession.hashCode() + "] " + longSession);
+        } else {
+            logger.info(whatStr + " Session: [" + longSession.hashCode() + "] ");
         }
         return longSession;
     }
@@ -341,8 +380,8 @@ public class ConversationHolder {
         // This will create a new session which must be explicitly managed by this conversation, which includes
         // binding / unbinding / closing session as well as starting / committing transactions.
         Session session = sessionFactory.openSession();
-        session.setFlushMode(FlushMode.COMMIT);
-        logger.info("Creating Session: [" + longSession + "]");
+        session.setFlushMode(getDefaultFlushMode());
+
         return session;
     }
 
@@ -420,5 +459,19 @@ public class ConversationHolder {
         return transactionStatus == null || transactionStatus.isCompleted();
     }
 
+    /**
+     * @return the defaultFlushMode
+     */
+    public FlushMode getDefaultFlushMode() {
+        return defaultFlushMode;
+    }
+
+    /**
+     * @param defaultFlushMode the defaultFlushMode to set
+     */
+    public void setDefaultFlushMode(FlushMode defaultFlushMode) {
+        this.defaultFlushMode = defaultFlushMode;
+    }
+
 
 }