KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > orm > ojb > PersistenceBrokerTransactionManager


1 /*
2  * Copyright 2002-2005 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.orm.ojb;
18
19 import java.sql.Connection JavaDoc;
20
21 import javax.sql.DataSource JavaDoc;
22
23 import org.apache.ojb.broker.OJBRuntimeException;
24 import org.apache.ojb.broker.PBKey;
25 import org.apache.ojb.broker.PersistenceBroker;
26 import org.apache.ojb.broker.PersistenceBrokerFactory;
27 import org.apache.ojb.broker.TransactionAbortedException;
28 import org.apache.ojb.broker.accesslayer.LookupException;
29
30 import org.springframework.jdbc.datasource.ConnectionHolder;
31 import org.springframework.jdbc.datasource.DataSourceUtils;
32 import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
33 import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
34 import org.springframework.transaction.CannotCreateTransactionException;
35 import org.springframework.transaction.IllegalTransactionStateException;
36 import org.springframework.transaction.TransactionDefinition;
37 import org.springframework.transaction.TransactionSystemException;
38 import org.springframework.transaction.support.AbstractPlatformTransactionManager;
39 import org.springframework.transaction.support.DefaultTransactionStatus;
40 import org.springframework.transaction.support.TransactionSynchronizationManager;
41
42 /**
43  * PlatformTransactionManager implementation for a single OJB persistence broker key.
44  * Binds an OJB PersistenceBroker from the specified key to the thread, potentially
45  * allowing for one thread PersistenceBroker per key. OjbFactoryUtils and
46  * PersistenceBrokerTemplate are aware of thread-bound persistence brokers and
47  * participate in such transactions automatically. Using either is required for
48  * OJB access code supporting this transaction management mechanism.
49  *
50  * <p>This implementation is appropriate for applications that solely use OJB for
51  * transactional data access. JTA (usually through JtaTransactionManager) is necessary
52  * for accessing multiple transactional resources, in combination with transactional
53  * DataSources as connection pools (to be specified in OJB's configuration).
54  *
55  * @author Juergen Hoeller
56  * @since 1.1
57  * @see #setJcdAlias
58  * @see #setPbKey
59  * @see OjbFactoryUtils#getPersistenceBroker
60  * @see OjbFactoryUtils#releasePersistenceBroker
61  * @see PersistenceBrokerTemplate#execute
62  * @see org.springframework.transaction.jta.JtaTransactionManager
63  */

64 public class PersistenceBrokerTransactionManager extends AbstractPlatformTransactionManager {
65
66     private PBKey pbKey = PersistenceBrokerFactory.getDefaultKey();
67
68     private DataSource JavaDoc dataSource;
69
70
71     /**
72      * Create a new PersistenceBrokerTransactionManager,
73      * sing the default connection configured for OJB.
74      */

75     public PersistenceBrokerTransactionManager() {
76     }
77
78     /**
79      * Create a new PersistenceBrokerTransactionManager.
80      * @param jcdAlias the JDBC Connection Descriptor alias
81      * of the PersistenceBroker configuration to use
82      */

83     public PersistenceBrokerTransactionManager(String JavaDoc jcdAlias) {
84         setJcdAlias(jcdAlias);
85     }
86
87     /**
88      * Create a new PersistenceBrokerTransactionManager.
89      * @param pbKey the PBKey of the PersistenceBroker configuration to use
90      */

91     public PersistenceBrokerTransactionManager(PBKey pbKey) {
92         setPbKey(pbKey);
93     }
94
95     /**
96      * Set the JDBC Connection Descriptor alias of the PersistenceBroker
97      * configuration to use. Default is the default connection configured for OJB.
98      */

99     public void setJcdAlias(String JavaDoc jcdAlias) {
100         this.pbKey = new PBKey(jcdAlias);
101     }
102
103     /**
104      * Set the PBKey of the PersistenceBroker configuration to use.
105      * Default is the default connection configured for OJB.
106      */

107     public void setPbKey(PBKey pbKey) {
108         this.pbKey = pbKey;
109     }
110
111     /**
112      * Return the PBKey of the PersistenceBroker configuration used.
113      */

114     public PBKey getPbKey() {
115         return pbKey;
116     }
117
118     /**
119      * Set the JDBC DataSource that this instance should manage transactions for.
120      * The DataSource should match the one configured for the OJB JCD alias:
121      * for example, you could specify the same JNDI DataSource for both.
122      * <p>A transactional JDBC Connection for this DataSource will be provided to
123      * application code accessing this DataSource directly via DataSourceUtils
124      * or JdbcTemplate. The Connection will be taken from the Hibernate Session.
125      * <p>The DataSource specified here should be the target DataSource to manage
126      * transactions for, not a TransactionAwareDataSourceProxy. Only data access
127      * code may work with TransactionAwareDataSourceProxy, while the transaction
128      * manager needs to work on the underlying target DataSource. If there's
129      * nevertheless a TransactionAwareDataSourceProxy passed in, it will be
130      * unwrapped to extract its target DataSource.
131      * @see org.springframework.orm.hibernate.LocalDataSourceConnectionProvider
132      * @see org.springframework.orm.hibernate.LocalSessionFactoryBean#setDataSource
133      * @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
134      * @see org.springframework.jdbc.core.JdbcTemplate
135      * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
136      */

137     public void setDataSource(DataSource JavaDoc dataSource) {
138         if (dataSource instanceof TransactionAwareDataSourceProxy) {
139             // If we got a TransactionAwareDataSourceProxy, we need to perform transactions
140
// for its underlying target DataSource, else data access code won't see
141
// properly exposed transactions (i.e. transactions for the target DataSource).
142
this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
143         }
144         else {
145             this.dataSource = dataSource;
146         }
147     }
148
149     /**
150      * Return the JDBC DataSource that this instance manages transactions for.
151      */

152     public DataSource JavaDoc getDataSource() {
153         return dataSource;
154     }
155
156
157     protected Object JavaDoc doGetTransaction() {
158         PersistenceBrokerTransactionObject txObject = new PersistenceBrokerTransactionObject();
159         PersistenceBrokerHolder pbHolder =
160             (PersistenceBrokerHolder) TransactionSynchronizationManager.getResource(getPbKey());
161         txObject.setPersistenceBrokerHolder(pbHolder);
162         return txObject;
163     }
164
165     protected boolean isExistingTransaction(Object JavaDoc transaction) {
166         PersistenceBrokerTransactionObject txObject = (PersistenceBrokerTransactionObject) transaction;
167         return (txObject.getPersistenceBrokerHolder() != null);
168     }
169
170     protected void doBegin(Object JavaDoc transaction, TransactionDefinition definition) {
171         if (getDataSource() != null && TransactionSynchronizationManager.hasResource(getDataSource())) {
172             throw new IllegalTransactionStateException(
173                     "Pre-bound JDBC Connection found - PersistenceBrokerTransactionManager does not support " +
174                     "running within DataSourceTransactionManager if told to manage the DataSource itself. " +
175                     "It is recommended to use a single PersistenceBrokerTransactionManager for all transactions " +
176                     "on a single DataSource, no matter whether PersistenceBroker or JDBC access.");
177         }
178
179         PersistenceBroker pb = null;
180
181         try {
182             pb = getPersistenceBroker();
183             if (logger.isDebugEnabled()) {
184                 logger.debug("Opened new PersistenceBroker [" + pb + "] for OJB transaction");
185             }
186
187             PersistenceBrokerTransactionObject txObject = (PersistenceBrokerTransactionObject) transaction;
188             txObject.setPersistenceBrokerHolder(new PersistenceBrokerHolder(pb));
189
190             Connection JavaDoc con = pb.serviceConnectionManager().getConnection();
191             Integer JavaDoc previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
192             txObject.setPreviousIsolationLevel(previousIsolationLevel);
193
194             pb.beginTransaction();
195
196             // Register the OJB PersistenceBroker's JDBC Connection for the DataSource, if set.
197
if (getDataSource() != null) {
198                 ConnectionHolder conHolder = new ConnectionHolder(con);
199                 if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
200                     conHolder.setTimeoutInSeconds(definition.getTimeout());
201                 }
202                 if (logger.isDebugEnabled()) {
203                     logger.debug("Exposing OJB transaction as JDBC transaction [" + conHolder.getConnection() + "]");
204                 }
205                 TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
206                 txObject.setConnectionHolder(conHolder);
207             }
208
209             // Bind the persistence broker holder to the thread.
210
TransactionSynchronizationManager.bindResource(getPbKey(), txObject.getPersistenceBrokerHolder());
211         }
212
213         catch (Exception JavaDoc ex) {
214             releasePersistenceBroker(pb);
215             throw new CannotCreateTransactionException("Could not open OJB PersistenceBroker for transaction", ex);
216         }
217     }
218
219     protected Object JavaDoc doSuspend(Object JavaDoc transaction) {
220         PersistenceBrokerTransactionObject txObject = (PersistenceBrokerTransactionObject) transaction;
221         txObject.setPersistenceBrokerHolder(null);
222         PersistenceBrokerHolder pbHolder =
223                 (PersistenceBrokerHolder) TransactionSynchronizationManager.unbindResource(getPbKey());
224         ConnectionHolder connectionHolder = null;
225         if (getDataSource() != null) {
226             connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource());
227         }
228         return new SuspendedResourcesHolder(pbHolder, connectionHolder);
229     }
230
231     protected void doResume(Object JavaDoc transaction, Object JavaDoc suspendedResources) {
232         SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
233         if (TransactionSynchronizationManager.hasResource(getPbKey())) {
234             // From non-transactional code running in active transaction synchronization
235
// -> can be safely removed, will be closed on transaction completion.
236
TransactionSynchronizationManager.unbindResource(getPbKey());
237         }
238         TransactionSynchronizationManager.bindResource(getPbKey(), resourcesHolder.getPersistenceBrokerHolder());
239         if (getDataSource() != null) {
240             TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder());
241         }
242     }
243
244     protected void doCommit(DefaultTransactionStatus status) {
245         PersistenceBrokerTransactionObject txObject = (PersistenceBrokerTransactionObject) status.getTransaction();
246         if (status.isDebug()) {
247             logger.debug("Committing OJB transaction on PersistenceBroker [" +
248                     txObject.getPersistenceBrokerHolder().getPersistenceBroker() + "]");
249         }
250         try {
251             txObject.getPersistenceBrokerHolder().getPersistenceBroker().commitTransaction();
252         }
253         catch (TransactionAbortedException ex) {
254             // assumably from commit call to underlying JDBC connection
255
throw new TransactionSystemException("Could not commit OJB transaction", ex);
256         }
257     }
258
259     protected void doRollback(DefaultTransactionStatus status) {
260         PersistenceBrokerTransactionObject txObject = (PersistenceBrokerTransactionObject) status.getTransaction();
261         if (status.isDebug()) {
262             logger.debug("Rolling back OJB transaction on PersistenceBroker [" +
263                     txObject.getPersistenceBrokerHolder().getPersistenceBroker() + "]");
264         }
265         txObject.getPersistenceBrokerHolder().getPersistenceBroker().abortTransaction();
266     }
267
268     protected void doSetRollbackOnly(DefaultTransactionStatus status) {
269         PersistenceBrokerTransactionObject txObject = (PersistenceBrokerTransactionObject) status.getTransaction();
270         if (status.isDebug()) {
271             logger.debug("Setting OJB transaction on PersistenceBroker [" +
272                     txObject.getPersistenceBrokerHolder().getPersistenceBroker() + "] rollback-only");
273         }
274         txObject.setRollbackOnly();
275     }
276
277     protected void doCleanupAfterCompletion(Object JavaDoc transaction) {
278         PersistenceBrokerTransactionObject txObject = (PersistenceBrokerTransactionObject) transaction;
279
280         // Remove the persistence broker holder from the thread.
281
TransactionSynchronizationManager.unbindResource(getPbKey());
282         txObject.getPersistenceBrokerHolder().clear();
283
284         // Remove the JDBC connection holder from the thread, if set.
285
if (getDataSource() != null) {
286             TransactionSynchronizationManager.unbindResource(getDataSource());
287         }
288
289         PersistenceBroker pb = txObject.getPersistenceBrokerHolder().getPersistenceBroker();
290         try {
291             Connection JavaDoc con = pb.serviceConnectionManager().getConnection();
292             DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
293         }
294         catch (LookupException ex) {
295             logger.info("Could not look up JDBC Connection of OJB PersistenceBroker", ex);
296         }
297
298         if (logger.isDebugEnabled()) {
299             logger.debug("Closing OJB PersistenceBroker [" + pb + "] after transaction");
300         }
301         releasePersistenceBroker(pb);
302     }
303
304
305     /**
306      * Get an OJB PersistenceBroker for the PBKey of this transaction manager.
307      * <p>Default implementation simply creates a new PersistenceBroker.
308      * Can be overridden in subclasses, e.g. for testing purposes.
309      * @return the PersistenceBroker
310      * @throws OJBRuntimeException if PersistenceBroker cretion failed
311      * @see #setJcdAlias
312      * @see #setPbKey
313      * @see org.apache.ojb.broker.PersistenceBrokerFactory#createPersistenceBroker(org.apache.ojb.broker.PBKey)
314      */

315     protected PersistenceBroker getPersistenceBroker() throws OJBRuntimeException {
316         return PersistenceBrokerFactory.createPersistenceBroker(getPbKey());
317     }
318
319     /**
320      * Close the given PersistenceBroker, created for the PBKey of this
321      * transaction manager, if it isn't bound to the thread.
322      * <p>Default implementation delegates to OjbFactoryUtils.
323      * Can be overridden in subclasses, e.g. for testing purposes.
324      * @param pb PersistenceBroker to close
325      * @see #setJcdAlias
326      * @see #setPbKey
327      * @see OjbFactoryUtils#releasePersistenceBroker
328      */

329     protected void releasePersistenceBroker(PersistenceBroker pb) {
330         OjbFactoryUtils.releasePersistenceBroker(pb, getPbKey());
331     }
332
333
334     /**
335      * OJB transaction object, representing a PersistenceBrokerHolder.
336      * Used as transaction object by PersistenceBrokerTransactionManager.
337      *
338      * <p>Derives from JdbcTransactionObjectSupport to inherit the capability
339      * to manage JDBC 3.0 Savepoints for underlying JDBC Connections.
340      *
341      * @see PersistenceBrokerHolder
342      */

343     private static class PersistenceBrokerTransactionObject extends JdbcTransactionObjectSupport {
344
345         private PersistenceBrokerHolder persistenceBrokerHolder;
346
347         public void setPersistenceBrokerHolder(PersistenceBrokerHolder persistenceBrokerHolder) {
348             this.persistenceBrokerHolder = persistenceBrokerHolder;
349         }
350
351         public PersistenceBrokerHolder getPersistenceBrokerHolder() {
352             return persistenceBrokerHolder;
353         }
354
355         public void setRollbackOnly() {
356             getPersistenceBrokerHolder().setRollbackOnly();
357             if (getConnectionHolder() != null) {
358                 getConnectionHolder().setRollbackOnly();
359             }
360         }
361
362         public boolean isRollbackOnly() {
363             return getPersistenceBrokerHolder().isRollbackOnly() ||
364                     (getConnectionHolder() != null && getConnectionHolder().isRollbackOnly());
365         }
366     }
367
368
369     /**
370      * Holder for suspended resources.
371      * Used internally by doSuspend and doResume.
372      */

373     private static class SuspendedResourcesHolder {
374
375         private final PersistenceBrokerHolder persistenceBrokerHolder;
376
377         private final ConnectionHolder connectionHolder;
378
379         private SuspendedResourcesHolder(PersistenceBrokerHolder pbHolder, ConnectionHolder conHolder) {
380             this.persistenceBrokerHolder = pbHolder;
381             this.connectionHolder = conHolder;
382         }
383
384         private PersistenceBrokerHolder getPersistenceBrokerHolder() {
385             return persistenceBrokerHolder;
386         }
387
388         private ConnectionHolder getConnectionHolder() {
389             return connectionHolder;
390         }
391     }
392
393 }
394
Popular Tags