Project

General

Profile

Download (8.14 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2009 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
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.
8
*/ 
9

    
10
package eu.etaxonomy.cdm.api.conversation;
11

    
12
import javax.sql.DataSource;
13

    
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;
26

    
27
import eu.etaxonomy.cdm.persistence.hibernate.CdmPostDataChangeObservableListener;
28

    
29
/**
30
 * This is an implementation of the session-per-conversation pattern for usage
31
 * in a Spring context.
32
 *  
33
 * @see http://www.hibernate.org/42.html
34
 * 
35
 * @author n.hoffmann
36
 * @created 12.03.2009
37
 * @version 1.0
38
 */
39
public class ConversationHolder{
40

    
41
	private static final Logger logger = Logger.getLogger(ConversationHolder.class);
42

    
43
	@Autowired
44
	private SessionFactory sessionFactory;
45
	
46
	@Autowired
47
	private DataSource dataSource;
48
	
49
	@Autowired
50
	private PlatformTransactionManager transactionManager;
51

    
52

    
53
	
54
	/**
55
	 * The persistence context for this conversation
56
	 */
57
	private Session longSession = null;
58

    
59
	/**
60
	 * Spring communicates with hibernate sessions via a SessionHolder object
61
	 */
62
	private SessionHolder sessionHolder = null;
63

    
64
	/**
65
	 * @see TransactionDefinition
66
	 */
67
	private TransactionDefinition definition;
68
	
69
	/**
70
	 * This conversations transaction
71
	 */
72
	private TransactionStatus transactionStatus;
73

    
74
	private boolean closed = false;
75

    
76
	/**
77
	 * Simple constructor used by Spring only
78
	 */
79
	private ConversationHolder(){
80
		closed = false;
81
	}
82

    
83
	public ConversationHolder(DataSource dataSource, SessionFactory sessionFactory, 
84
			PlatformTransactionManager transactionManager) {
85
		this();
86
		this.dataSource = dataSource;
87
		this.sessionFactory = sessionFactory;
88
		this.transactionManager = transactionManager;
89
		
90
		bind();
91
		
92
		if(TransactionSynchronizationManager.hasResource(getDataSource())){
93
			TransactionSynchronizationManager.unbindResource(getDataSource());
94
		}
95
	}
96
	
97
	/**
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
100
	 */
101
	public void bind() {
102
		
103
		logger.info("Binding resources for ConversationHolder");	
104
				
105
		if(TransactionSynchronizationManager.isSynchronizationActive()){
106
			TransactionSynchronizationManager.clearSynchronization();
107
		}
108
		
109
		try{
110
			
111
			logger.info("Starting new Synchronization in TransactionSynchronizationManager");
112
			TransactionSynchronizationManager.initSynchronization();
113
			
114
			if(TransactionSynchronizationManager.hasResource(getSessionFactory())){
115
				TransactionSynchronizationManager.unbindResource(getSessionFactory());
116
			}
117
			
118
			logger.info("Binding Session to TransactionSynchronizationManager: Session: " + getSessionHolder());
119
			TransactionSynchronizationManager.bindResource(getSessionFactory(), getSessionHolder());
120
			
121
		}catch(Exception e){
122
			logger.error("Error binding resources for session", e);
123
		}			
124
		
125
	}
126
	
127
	public SessionHolder getSessionHolder(){
128
		if(this.sessionHolder == null){
129
			logger.info("Creating new SessionHolder");
130
			this.sessionHolder = new SessionHolder(getSession());
131
		}
132
		return this.sessionHolder;
133
	}
134
	
135
	/**
136
	 * @return
137
	 */
138
	private DataSource getDataSource() {
139
		return this.dataSource;
140
	}
141

    
142
	/**
143
	 * @return true if this longSession is bound to the session factory.
144
	 */
145
	public boolean isBound(){
146
		//return sessionHolder != null && longSession != null && longSession.isConnected();
147
		return longSession != null && getSessionFactory().getCurrentSession() == longSession;
148
	}
149
	
150
	/**
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.
153
	 * 
154
	 * @return the transaction status bound to this conversation holder
155
	 */
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");
161
		}else{				
162
			transactionStatus = transactionManager.getTransaction(definition);
163
			
164
			logger.info("Transaction started: " + transactionStatus);
165
		}
166
		return transactionStatus;
167
	}
168
	
169
	/** 
170
	 * @return if there is a running transaction
171
	 */
172
	public boolean isTransactionActive(){
173
		return transactionStatus != null;
174
	}
175
	
176
	/* (non-Javadoc)
177
	 * @see org.hibernate.Session#evict(java.lang.Object object)
178
	 */
179
	public void evict(Object object){
180
		getSession().evict(object);
181
	}
182
	
183
	/* (non-Javadoc)
184
	 * @see org.hibernate.Session#refresh(java.lang.Object object)
185
	 */
186
	public void refresh(Object object){
187
		getSession().refresh(object);
188
	}
189
	
190
	/* (non-Javadoc)
191
	 * @see org.hibernate.Session#clear()
192
	 */
193
	public void clear(){
194
		getSession().clear();
195
	}
196
	
197
	/**
198
	 * Commit the running transaction.
199
	 */
200
	public void commit(){
201
		commit(true);
202
	}
203
	
204
	/**
205
	 * Commit the running transaction but optionally start a
206
	 * new one right away.
207
	 * 
208
	 * @param restartTransaction whether to start a new transaction
209
	 */
210
	public TransactionStatus commit(boolean restartTransaction){
211
		if(isTransactionActive()){
212
			
213
			if(getSessionHolder().isRollbackOnly()){
214
				logger.error("Commiting this session will not work. It has been marked as rollback only.");
215
			}
216
			
217
			// commit the changes
218
			transactionManager.commit(transactionStatus);
219
			
220
			// propagate transaction end
221
			CdmPostDataChangeObservableListener.getDefault().delayedNotify();	
222
			
223
			// Reset the transactionStatus.
224
			transactionStatus = null;
225
			
226
			// Committing a transaction frees all resources.
227
			// Since we are in a conversation we directly rebind those resources and start a new transaction
228
			bind();
229
			if(restartTransaction){
230
				return startTransaction();
231
			}
232
		}else{
233
			logger.warn("No active transaction but commit was called");
234
		}
235
		return null;
236
	}
237

    
238
	/**
239
	 * @return the session associated with this conversation manager 
240
	 */
241
	private Session getSession() {
242
		if(longSession == null){
243
			logger.info("Creating Session: [" + longSession + "]");
244
			longSession = SessionFactoryUtils.getNewSession(getSessionFactory());
245
			longSession.setFlushMode(FlushMode.COMMIT);
246
		}
247
		
248
		return longSession;
249
	}
250
	
251
	/** 
252
	 * @return the session factory that is bound to this conversation manager
253
	 */
254
	private SessionFactory getSessionFactory() {
255
		return sessionFactory;
256
	}
257

    
258
	public void delete(Object object){
259
		this.getSession().delete(object);
260
	}
261
	
262
	/**
263
	 * Facades Session.lock()
264
	 */
265
	public void lock(Object persistentObject, LockMode lockMode) {
266
		getSession().lock(persistentObject, lockMode);
267
	}
268
	
269
	public void lock(String entityName, Object persistentObject, LockMode lockMode){
270
		getSession().lock(entityName, persistentObject, lockMode);
271
	}
272

    
273
	/**
274
	 * @return the definition
275
	 */
276
	public TransactionDefinition getDefinition() {
277
		return definition;
278
	}
279

    
280
	/**
281
	 * @param definition the definition to set
282
	 */
283
	public void setDefinition(TransactionDefinition definition) {
284
		this.definition = definition;
285
	}
286
	
287
	/**
288
	 * Register to get updated after any interaction with the datastore
289
	 */
290
	public void registerForDataStoreChanges(IConversationEnabled observer) {
291
		CdmPostDataChangeObservableListener.getDefault().register(observer);
292
	}
293
	
294
	/**
295
	 * Register to get updated after any interaction with the datastore
296
	 */
297
	public void unregisterForDataStoreChanges(IConversationEnabled observer) {
298
		CdmPostDataChangeObservableListener.getDefault().unregister(observer);
299
	}
300
	
301
	/**
302
	 * Free resources bound to this conversationHolder
303
	 */
304
	public void close(){
305
		if(getSession().isOpen())
306
			getSession().close();
307
		closed = true;
308
	}
309
	
310
	public boolean isClosed(){
311
		return closed;
312
	}
313
}
(1-1/2)