2 * Copyright (C) 2009 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.cdm
.api
.conversation
;
12 import javax
.sql
.DataSource
;
14 import org
.apache
.log4j
.Logger
;
15 import org
.hibernate
.FlushMode
;
16 import org
.hibernate
.LockMode
;
17 import org
.hibernate
.Session
;
18 import org
.hibernate
.SessionFactory
;
19 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
20 import org
.springframework
.orm
.hibernate3
.SessionFactoryUtils
;
21 import org
.springframework
.orm
.hibernate3
.SessionHolder
;
22 import org
.springframework
.transaction
.PlatformTransactionManager
;
23 import org
.springframework
.transaction
.TransactionDefinition
;
24 import org
.springframework
.transaction
.TransactionStatus
;
25 import org
.springframework
.transaction
.support
.TransactionSynchronizationManager
;
27 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.CdmPostDataChangeObservableListener
;
30 * This is an implementation of the session-per-conversation pattern for usage
31 * in a Spring context.
33 * @see http://www.hibernate.org/42.html
39 public class ConversationHolder
{
41 private static final Logger logger
= Logger
.getLogger(ConversationHolder
.class);
44 private SessionFactory sessionFactory
;
47 private DataSource dataSource
;
50 private PlatformTransactionManager transactionManager
;
55 * The persistence context for this conversation
57 private Session longSession
= null;
60 * Spring communicates with hibernate sessions via a SessionHolder object
62 private SessionHolder sessionHolder
= null;
65 * @see TransactionDefinition
67 private TransactionDefinition definition
;
70 * This conversations transaction
72 private TransactionStatus transactionStatus
;
74 private boolean closed
= false;
77 * Simple constructor used by Spring only
79 private ConversationHolder(){
83 public ConversationHolder(DataSource dataSource
, SessionFactory sessionFactory
,
84 PlatformTransactionManager transactionManager
) {
86 this.dataSource
= dataSource
;
87 this.sessionFactory
= sessionFactory
;
88 this.transactionManager
= transactionManager
;
92 if(TransactionSynchronizationManager
.hasResource(getDataSource())){
93 TransactionSynchronizationManager
.unbindResource(getDataSource());
98 * This method has to be called when starting a new unit-of-work. All required resources are
99 * bound so that SessionFactory.getCurrentSession() returns the right session for this conversation
103 logger
.info("Binding resources for ConversationHolder");
105 if(TransactionSynchronizationManager
.isSynchronizationActive()){
106 TransactionSynchronizationManager
.clearSynchronization();
111 logger
.info("Starting new Synchronization in TransactionSynchronizationManager");
112 TransactionSynchronizationManager
.initSynchronization();
114 if(TransactionSynchronizationManager
.hasResource(getSessionFactory())){
115 TransactionSynchronizationManager
.unbindResource(getSessionFactory());
118 logger
.info("Binding Session to TransactionSynchronizationManager: Session: " + getSessionHolder());
119 TransactionSynchronizationManager
.bindResource(getSessionFactory(), getSessionHolder());
122 logger
.error("Error binding resources for session", e
);
127 public SessionHolder
getSessionHolder(){
128 if(this.sessionHolder
== null){
129 logger
.info("Creating new SessionHolder");
130 this.sessionHolder
= new SessionHolder(getSession());
132 return this.sessionHolder
;
138 private DataSource
getDataSource() {
139 return this.dataSource
;
143 * @return true if this longSession is bound to the session factory.
145 public boolean isBound(){
146 //return sessionHolder != null && longSession != null && longSession.isConnected();
147 return longSession
!= null && getSessionFactory().getCurrentSession() == longSession
;
151 * Creates an instance of TransactionStatus and binds it to this conversation manager.
152 * At the moment we allow only on transaction per conversation holder.
154 * @return the transaction status bound to this conversation holder
156 public TransactionStatus
startTransaction(){
157 if (isTransactionActive()){
158 logger
.warn("We allow only one transaction at the moment but startTransaction " +
159 "was called a second time.\nReturning the transaction already associated with this " +
160 "ConversationManager");
162 transactionStatus
= transactionManager
.getTransaction(definition
);
164 logger
.info("Transaction started: " + transactionStatus
);
166 return transactionStatus
;
170 * @return if there is a running transaction
172 public boolean isTransactionActive(){
173 return transactionStatus
!= null;
177 * @see org.hibernate.Session#evict(java.lang.Object object)
179 public void evict(Object object
){
180 getSession().evict(object
);
184 * @see org.hibernate.Session#refresh(java.lang.Object object)
186 public void refresh(Object object
){
187 getSession().refresh(object
);
191 * @see org.hibernate.Session#clear()
194 getSession().clear();
198 * Commit the running transaction.
200 public void commit(){
205 * Commit the running transaction but optionally start a
206 * new one right away.
208 * @param restartTransaction whether to start a new transaction
210 public TransactionStatus
commit(boolean restartTransaction
){
211 if(isTransactionActive()){
213 if(getSessionHolder().isRollbackOnly()){
214 logger
.error("Commiting this session will not work. It has been marked as rollback only.");
217 // commit the changes
218 transactionManager
.commit(transactionStatus
);
220 // propagate transaction end
221 CdmPostDataChangeObservableListener
.getDefault().delayedNotify();
223 // Reset the transactionStatus.
224 transactionStatus
= null;
226 // Committing a transaction frees all resources.
227 // Since we are in a conversation we directly rebind those resources and start a new transaction
229 if(restartTransaction
){
230 return startTransaction();
233 logger
.warn("No active transaction but commit was called");
239 * @return the session associated with this conversation manager
241 private Session
getSession() {
242 if(longSession
== null){
243 logger
.info("Creating Session: [" + longSession
+ "]");
244 longSession
= SessionFactoryUtils
.getNewSession(getSessionFactory());
245 longSession
.setFlushMode(FlushMode
.COMMIT
);
252 * @return the session factory that is bound to this conversation manager
254 private SessionFactory
getSessionFactory() {
255 return sessionFactory
;
258 public void delete(Object object
){
259 this.getSession().delete(object
);
263 * Facades Session.lock()
265 public void lock(Object persistentObject
, LockMode lockMode
) {
266 getSession().lock(persistentObject
, lockMode
);
269 public void lock(String entityName
, Object persistentObject
, LockMode lockMode
){
270 getSession().lock(entityName
, persistentObject
, lockMode
);
274 * @return the definition
276 public TransactionDefinition
getDefinition() {
281 * @param definition the definition to set
283 public void setDefinition(TransactionDefinition definition
) {
284 this.definition
= definition
;
288 * Register to get updated after any interaction with the datastore
290 public void registerForDataStoreChanges(IConversationEnabled observer
) {
291 CdmPostDataChangeObservableListener
.getDefault().register(observer
);
295 * Register to get updated after any interaction with the datastore
297 public void unregisterForDataStoreChanges(IConversationEnabled observer
) {
298 CdmPostDataChangeObservableListener
.getDefault().unregister(observer
);
302 * Free resources bound to this conversationHolder
305 if(getSession().isOpen())
306 getSession().close();
310 public boolean isClosed(){