001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.orm.jdo;
018:
019: import javax.jdo.JDOException;
020: import javax.jdo.PersistenceManager;
021: import javax.jdo.PersistenceManagerFactory;
022: import javax.jdo.Transaction;
023: import javax.sql.DataSource;
024:
025: import org.springframework.beans.factory.InitializingBean;
026: import org.springframework.dao.DataAccessException;
027: import org.springframework.jdbc.datasource.ConnectionHandle;
028: import org.springframework.jdbc.datasource.ConnectionHolder;
029: import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
030: import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
031: import org.springframework.transaction.CannotCreateTransactionException;
032: import org.springframework.transaction.IllegalTransactionStateException;
033: import org.springframework.transaction.TransactionDefinition;
034: import org.springframework.transaction.TransactionException;
035: import org.springframework.transaction.TransactionSystemException;
036: import org.springframework.transaction.support.AbstractPlatformTransactionManager;
037: import org.springframework.transaction.support.DefaultTransactionStatus;
038: import org.springframework.transaction.support.ResourceTransactionManager;
039: import org.springframework.transaction.support.TransactionSynchronizationManager;
040:
041: /**
042: * {@link org.springframework.transaction.PlatformTransactionManager} implementation
043: * for a single JDO {@link javax.jdo.PersistenceManagerFactory}. Binds a JDO
044: * PersistenceManager from the specified factory to the thread, potentially allowing
045: * for one thread-bound PersistenceManager per factory.
046: * {@link PersistenceManagerFactoryUtils} and {@link JdoTemplate} are aware of
047: * thread-bound persistence managers and participate in such transactions automatically.
048: * Using either of those (or going through a {@link TransactionAwarePersistenceManagerFactoryProxy}
049: * is required for JDO access code supporting this transaction management mechanism.
050: *
051: * <p>This transaction manager is appropriate for applications that use a single
052: * JDO PersistenceManagerFactory for transactional data access. JTA (usually through
053: * {@link org.springframework.transaction.jta.JtaTransactionManager}) is necessary
054: * for accessing multiple transactional resources within the same transaction.
055: * Note that you need to configure your JDO provider accordingly in order to make
056: * it participate in JTA transactions.
057: *
058: * <p>This transaction manager also supports direct DataSource access within a
059: * transaction (i.e. plain JDBC code working with the same DataSource).
060: * This allows for mixing services which access JDO and services which use plain
061: * JDBC (without being aware of JDO)! Application code needs to stick to the
062: * same simple Connection lookup pattern as with
063: * {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
064: * (i.e. {@link org.springframework.jdbc.datasource.DataSourceUtils#getConnection}
065: * or going through a
066: * {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}).
067: *
068: * <p>Note: To be able to register a DataSource's Connection for plain JDBC code,
069: * this instance needs to be aware of the DataSource ({@link #setDataSource}).
070: * The given DataSource should obviously match the one used by the given
071: * PersistenceManagerFactory. This transaction manager will autodetect the DataSource
072: * that acts as "connectionFactory" of the PersistenceManagerFactory, so you usually
073: * don't need to explicitly specify the "dataSource" property.
074: *
075: * <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
076: * Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
077: * flag defaults to "false", though, as nested transactions will just apply to the
078: * JDBC Connection, not to the JDO PersistenceManager and its cached objects.
079: * You can manually set the flag to "true" if you want to use nested transactions
080: * for JDBC access code which participates in JDO transactions (provided that your
081: * JDBC driver supports Savepoints). <i>Note that JDO itself does not support
082: * nested transactions! Hence, do not expect JDO access code to semantically
083: * participate in a nested transaction.</i>
084: *
085: * @author Juergen Hoeller
086: * @since 03.06.2003
087: * @see #setPersistenceManagerFactory
088: * @see #setDataSource
089: * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory
090: * @see LocalPersistenceManagerFactoryBean
091: * @see PersistenceManagerFactoryUtils#getPersistenceManager
092: * @see PersistenceManagerFactoryUtils#releasePersistenceManager
093: * @see JdoTemplate
094: * @see TransactionAwarePersistenceManagerFactoryProxy
095: * @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
096: * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
097: * @see org.springframework.jdbc.core.JdbcTemplate
098: * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
099: * @see org.springframework.transaction.jta.JtaTransactionManager
100: */
101: public class JdoTransactionManager extends
102: AbstractPlatformTransactionManager implements
103: ResourceTransactionManager, InitializingBean {
104:
105: private static boolean jdoSetRollbackOnlyAvailable;
106:
107: static {
108: // Determine whether the JDO 2.0 Transaction.setRollbackOnly method
109: // is available, for use in this JdoTransactionManager.
110: try {
111: Transaction.class
112: .getMethod("setRollbackOnly", new Class[0]);
113: jdoSetRollbackOnlyAvailable = true;
114: } catch (NoSuchMethodException ex) {
115: jdoSetRollbackOnlyAvailable = false;
116: }
117: }
118:
119: private PersistenceManagerFactory persistenceManagerFactory;
120:
121: private DataSource dataSource;
122:
123: private boolean autodetectDataSource = true;
124:
125: private JdoDialect jdoDialect;
126:
127: /**
128: * Create a new JdoTransactionManager instance.
129: * A PersistenceManagerFactory has to be set to be able to use it.
130: * @see #setPersistenceManagerFactory
131: */
132: public JdoTransactionManager() {
133: }
134:
135: /**
136: * Create a new JdoTransactionManager instance.
137: * @param pmf PersistenceManagerFactory to manage transactions for
138: */
139: public JdoTransactionManager(PersistenceManagerFactory pmf) {
140: this .persistenceManagerFactory = pmf;
141: afterPropertiesSet();
142: }
143:
144: /**
145: * Set the PersistenceManagerFactory that this instance should manage transactions for.
146: * <p>The PersistenceManagerFactory specified here should be the target
147: * PersistenceManagerFactory to manage transactions for, not a
148: * TransactionAwarePersistenceManagerFactoryProxy. Only data access
149: * code may work with TransactionAwarePersistenceManagerFactoryProxy, while the
150: * transaction manager needs to work on the underlying target PersistenceManagerFactory.
151: * @see TransactionAwarePersistenceManagerFactoryProxy
152: */
153: public void setPersistenceManagerFactory(
154: PersistenceManagerFactory pmf) {
155: this .persistenceManagerFactory = pmf;
156: }
157:
158: /**
159: * Return the PersistenceManagerFactory that this instance should manage transactions for.
160: */
161: public PersistenceManagerFactory getPersistenceManagerFactory() {
162: return this .persistenceManagerFactory;
163: }
164:
165: /**
166: * Set the JDBC DataSource that this instance should manage transactions for.
167: * The DataSource should match the one used by the JDO PersistenceManagerFactory:
168: * for example, you could specify the same JNDI DataSource for both.
169: * <p>If the PersistenceManagerFactory uses a DataSource as connection factory,
170: * the DataSource will be autodetected: You can still explictly specify the
171: * DataSource, but you don't need to in this case.
172: * <p>A transactional JDBC Connection for this DataSource will be provided to
173: * application code accessing this DataSource directly via DataSourceUtils
174: * or JdbcTemplate. The Connection will be taken from the JDO PersistenceManager.
175: * <p>Note that you need to use a JDO dialect for a specific JDO provider to
176: * allow for exposing JDO transactions as JDBC transactions.
177: * <p>The DataSource specified here should be the target DataSource to manage
178: * transactions for, not a TransactionAwareDataSourceProxy. Only data access
179: * code may work with TransactionAwareDataSourceProxy, while the transaction
180: * manager needs to work on the underlying target DataSource. If there's
181: * nevertheless a TransactionAwareDataSourceProxy passed in, it will be
182: * unwrapped to extract its target DataSource.
183: * @see #setAutodetectDataSource
184: * @see #setJdoDialect
185: * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory
186: * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
187: * @see org.springframework.jdbc.datasource.DataSourceUtils
188: * @see org.springframework.jdbc.core.JdbcTemplate
189: */
190: public void setDataSource(DataSource dataSource) {
191: if (dataSource instanceof TransactionAwareDataSourceProxy) {
192: // If we got a TransactionAwareDataSourceProxy, we need to perform transactions
193: // for its underlying target DataSource, else data access code won't see
194: // properly exposed transactions (i.e. transactions for the target DataSource).
195: this .dataSource = ((TransactionAwareDataSourceProxy) dataSource)
196: .getTargetDataSource();
197: } else {
198: this .dataSource = dataSource;
199: }
200: }
201:
202: /**
203: * Return the JDBC DataSource that this instance manages transactions for.
204: */
205: public DataSource getDataSource() {
206: return this .dataSource;
207: }
208:
209: /**
210: * Set whether to autodetect a JDBC DataSource used by the JDO PersistenceManagerFactory,
211: * as returned by the <code>getConnectionFactory()</code> method. Default is "true".
212: * <p>Can be turned off to deliberately ignore an available DataSource,
213: * to not expose JDO transactions as JDBC transactions for that DataSource.
214: * @see #setDataSource
215: * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory
216: */
217: public void setAutodetectDataSource(boolean autodetectDataSource) {
218: this .autodetectDataSource = autodetectDataSource;
219: }
220:
221: /**
222: * Set the JDO dialect to use for this transaction manager.
223: * <p>The dialect object can be used to retrieve the underlying JDBC connection
224: * and thus allows for exposing JDO transactions as JDBC transactions.
225: * @see JdoDialect#getJdbcConnection
226: */
227: public void setJdoDialect(JdoDialect jdoDialect) {
228: this .jdoDialect = jdoDialect;
229: }
230:
231: /**
232: * Return the JDO dialect to use for this transaction manager.
233: * <p>Creates a default one for the specified PersistenceManagerFactory if none set.
234: */
235: public JdoDialect getJdoDialect() {
236: if (this .jdoDialect == null) {
237: this .jdoDialect = new DefaultJdoDialect();
238: }
239: return this .jdoDialect;
240: }
241:
242: /**
243: * Eagerly initialize the JDO dialect, creating a default one
244: * for the specified PersistenceManagerFactory if none set.
245: * Auto-detect the PersistenceManagerFactory's DataSource, if any.
246: */
247: public void afterPropertiesSet() {
248: if (getPersistenceManagerFactory() == null) {
249: throw new IllegalArgumentException(
250: "Property 'persistenceManagerFactory' is required");
251: }
252: // Build default JdoDialect if none explicitly specified.
253: if (this .jdoDialect == null) {
254: this .jdoDialect = new DefaultJdoDialect(
255: getPersistenceManagerFactory()
256: .getConnectionFactory());
257: }
258:
259: // Check for DataSource as connection factory.
260: if (this .autodetectDataSource && getDataSource() == null) {
261: Object pmfcf = getPersistenceManagerFactory()
262: .getConnectionFactory();
263: if (pmfcf instanceof DataSource) {
264: // Use the PersistenceManagerFactory's DataSource for exposing transactions to JDBC code.
265: this .dataSource = (DataSource) pmfcf;
266: if (logger.isInfoEnabled()) {
267: logger
268: .info("Using DataSource ["
269: + this .dataSource
270: + "] of JDO PersistenceManagerFactory for JdoTransactionManager");
271: }
272: }
273: }
274: }
275:
276: public Object getResourceFactory() {
277: return getPersistenceManagerFactory();
278: }
279:
280: protected Object doGetTransaction() {
281: JdoTransactionObject txObject = new JdoTransactionObject();
282: txObject.setSavepointAllowed(isNestedTransactionAllowed());
283:
284: PersistenceManagerHolder pmHolder = (PersistenceManagerHolder) TransactionSynchronizationManager
285: .getResource(getPersistenceManagerFactory());
286: if (pmHolder != null) {
287: if (logger.isDebugEnabled()) {
288: logger.debug("Found thread-bound PersistenceManager ["
289: + pmHolder.getPersistenceManager()
290: + "] for JDO transaction");
291: }
292: txObject.setPersistenceManagerHolder(pmHolder, false);
293: }
294:
295: if (getDataSource() != null) {
296: ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager
297: .getResource(getDataSource());
298: txObject.setConnectionHolder(conHolder);
299: }
300:
301: return txObject;
302: }
303:
304: protected boolean isExistingTransaction(Object transaction) {
305: return ((JdoTransactionObject) transaction).hasTransaction();
306: }
307:
308: protected void doBegin(Object transaction,
309: TransactionDefinition definition) {
310: JdoTransactionObject txObject = (JdoTransactionObject) transaction;
311:
312: if (txObject.hasConnectionHolder()
313: && !txObject.getConnectionHolder()
314: .isSynchronizedWithTransaction()) {
315: throw new IllegalTransactionStateException(
316: "Pre-bound JDBC Connection found! JdoTransactionManager does not support "
317: + "running within DataSourceTransactionManager if told to manage the DataSource itself. "
318: + "It is recommended to use a single JdoTransactionManager for all transactions "
319: + "on a single DataSource, no matter whether JDO or JDBC access.");
320: }
321:
322: PersistenceManager pm = null;
323:
324: try {
325: if (txObject.getPersistenceManagerHolder() == null
326: || txObject.getPersistenceManagerHolder()
327: .isSynchronizedWithTransaction()) {
328: PersistenceManager newPm = getPersistenceManagerFactory()
329: .getPersistenceManager();
330: if (logger.isDebugEnabled()) {
331: logger.debug("Opened new PersistenceManager ["
332: + newPm + "] for JDO transaction");
333: }
334: txObject.setPersistenceManagerHolder(
335: new PersistenceManagerHolder(newPm), true);
336: }
337:
338: txObject.getPersistenceManagerHolder()
339: .setSynchronizedWithTransaction(true);
340: pm = txObject.getPersistenceManagerHolder()
341: .getPersistenceManager();
342:
343: // Delegate to JdoDialect for actual transaction begin.
344: Object transactionData = getJdoDialect().beginTransaction(
345: pm.currentTransaction(), definition);
346: txObject.setTransactionData(transactionData);
347:
348: // Register transaction timeout.
349: int timeout = determineTimeout(definition);
350: if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
351: txObject.getPersistenceManagerHolder()
352: .setTimeoutInSeconds(timeout);
353: }
354:
355: // Register the JDO PersistenceManager's JDBC Connection for the DataSource, if set.
356: if (getDataSource() != null) {
357: ConnectionHandle conHandle = getJdoDialect()
358: .getJdbcConnection(pm, definition.isReadOnly());
359: if (conHandle != null) {
360: ConnectionHolder conHolder = new ConnectionHolder(
361: conHandle);
362: if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
363: conHolder.setTimeoutInSeconds(timeout);
364: }
365: if (logger.isDebugEnabled()) {
366: logger
367: .debug("Exposing JDO transaction as JDBC transaction ["
368: + conHolder
369: .getConnectionHandle()
370: + "]");
371: }
372: TransactionSynchronizationManager.bindResource(
373: getDataSource(), conHolder);
374: txObject.setConnectionHolder(conHolder);
375: } else {
376: if (logger.isDebugEnabled()) {
377: logger
378: .debug("Not exposing JDO transaction ["
379: + pm
380: + "] as JDBC transaction because JdoDialect ["
381: + getJdoDialect()
382: + "] does not support JDBC Connection retrieval");
383: }
384: }
385: }
386:
387: // Bind the persistence manager holder to the thread.
388: if (txObject.isNewPersistenceManagerHolder()) {
389: TransactionSynchronizationManager.bindResource(
390: getPersistenceManagerFactory(), txObject
391: .getPersistenceManagerHolder());
392: }
393: }
394:
395: catch (TransactionException ex) {
396: PersistenceManagerFactoryUtils.releasePersistenceManager(
397: pm, getPersistenceManagerFactory());
398: throw ex;
399: } catch (Exception ex) {
400: PersistenceManagerFactoryUtils.releasePersistenceManager(
401: pm, getPersistenceManagerFactory());
402: throw new CannotCreateTransactionException(
403: "Could not open JDO PersistenceManager for transaction",
404: ex);
405: }
406: }
407:
408: protected Object doSuspend(Object transaction) {
409: JdoTransactionObject txObject = (JdoTransactionObject) transaction;
410: txObject.setPersistenceManagerHolder(null, false);
411: PersistenceManagerHolder persistenceManagerHolder = (PersistenceManagerHolder) TransactionSynchronizationManager
412: .unbindResource(getPersistenceManagerFactory());
413: txObject.setConnectionHolder(null);
414: ConnectionHolder connectionHolder = null;
415: if (getDataSource() != null) {
416: connectionHolder = (ConnectionHolder) TransactionSynchronizationManager
417: .unbindResource(getDataSource());
418: }
419: return new SuspendedResourcesHolder(persistenceManagerHolder,
420: connectionHolder);
421: }
422:
423: protected void doResume(Object transaction,
424: Object suspendedResources) {
425: SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
426: TransactionSynchronizationManager.bindResource(
427: getPersistenceManagerFactory(), resourcesHolder
428: .getPersistenceManagerHolder());
429: if (getDataSource() != null) {
430: TransactionSynchronizationManager.bindResource(
431: getDataSource(), resourcesHolder
432: .getConnectionHolder());
433: }
434: }
435:
436: /**
437: * This implementation returns "true" for JDO 2.0: a JDO2 commit will properly
438: * handle transactions that have been marked rollback-only at a global level.
439: * On JDO 1.0, the rollback-only flag will be managed in Spring's resource holder.
440: */
441: protected boolean shouldCommitOnGlobalRollbackOnly() {
442: return jdoSetRollbackOnlyAvailable;
443: }
444:
445: protected void doCommit(DefaultTransactionStatus status) {
446: JdoTransactionObject txObject = (JdoTransactionObject) status
447: .getTransaction();
448: if (status.isDebug()) {
449: logger
450: .debug("Committing JDO transaction on PersistenceManager ["
451: + txObject.getPersistenceManagerHolder()
452: .getPersistenceManager() + "]");
453: }
454: try {
455: Transaction tx = txObject.getPersistenceManagerHolder()
456: .getPersistenceManager().currentTransaction();
457: tx.commit();
458: } catch (JDOException ex) {
459: // Assumably failed to flush changes to database.
460: throw convertJdoAccessException(ex);
461: }
462: }
463:
464: protected void doRollback(DefaultTransactionStatus status) {
465: JdoTransactionObject txObject = (JdoTransactionObject) status
466: .getTransaction();
467: if (status.isDebug()) {
468: logger
469: .debug("Rolling back JDO transaction on PersistenceManager ["
470: + txObject.getPersistenceManagerHolder()
471: .getPersistenceManager() + "]");
472: }
473: try {
474: Transaction tx = txObject.getPersistenceManagerHolder()
475: .getPersistenceManager().currentTransaction();
476: if (tx.isActive()) {
477: tx.rollback();
478: }
479: } catch (JDOException ex) {
480: throw new TransactionSystemException(
481: "Could not roll back JDO transaction", ex);
482: }
483: }
484:
485: protected void doSetRollbackOnly(DefaultTransactionStatus status) {
486: JdoTransactionObject txObject = (JdoTransactionObject) status
487: .getTransaction();
488: if (status.isDebug()) {
489: logger
490: .debug("Setting JDO transaction on PersistenceManager ["
491: + txObject.getPersistenceManagerHolder()
492: .getPersistenceManager()
493: + "] rollback-only");
494: }
495: txObject.setRollbackOnly();
496: }
497:
498: protected void doCleanupAfterCompletion(Object transaction) {
499: JdoTransactionObject txObject = (JdoTransactionObject) transaction;
500:
501: // Remove the persistence manager holder from the thread.
502: if (txObject.isNewPersistenceManagerHolder()) {
503: TransactionSynchronizationManager
504: .unbindResource(getPersistenceManagerFactory());
505: }
506: txObject.getPersistenceManagerHolder().clear();
507:
508: // Remove the JDBC connection holder from the thread, if exposed.
509: if (txObject.hasConnectionHolder()) {
510: TransactionSynchronizationManager
511: .unbindResource(getDataSource());
512: try {
513: getJdoDialect().releaseJdbcConnection(
514: txObject.getConnectionHolder()
515: .getConnectionHandle(),
516: txObject.getPersistenceManagerHolder()
517: .getPersistenceManager());
518: } catch (Throwable ex) {
519: // Just log it, to keep a transaction-related exception.
520: logger
521: .debug(
522: "Could not release JDBC connection after transaction",
523: ex);
524: }
525: }
526:
527: getJdoDialect().cleanupTransaction(
528: txObject.getTransactionData());
529:
530: if (txObject.isNewPersistenceManagerHolder()) {
531: PersistenceManager pm = txObject
532: .getPersistenceManagerHolder()
533: .getPersistenceManager();
534: if (logger.isDebugEnabled()) {
535: logger.debug("Closing JDO PersistenceManager [" + pm
536: + "] after transaction");
537: }
538: PersistenceManagerFactoryUtils.releasePersistenceManager(
539: pm, getPersistenceManagerFactory());
540: } else {
541: logger
542: .debug("Not closing pre-bound JDO PersistenceManager after transaction");
543: }
544: }
545:
546: /**
547: * Convert the given JDOException to an appropriate exception from the
548: * <code>org.springframework.dao</code> hierarchy.
549: * <p>Default implementation delegates to the JdoDialect.
550: * May be overridden in subclasses.
551: * @param ex JDOException that occured
552: * @return the corresponding DataAccessException instance
553: * @see JdoDialect#translateException
554: */
555: protected DataAccessException convertJdoAccessException(
556: JDOException ex) {
557: return getJdoDialect().translateException(ex);
558: }
559:
560: /**
561: * JDO transaction object, representing a PersistenceManagerHolder.
562: * Used as transaction object by JdoTransactionManager.
563: *
564: * <p>Derives from JdbcTransactionObjectSupport in order to inherit the
565: * capability to manage JDBC 3.0 Savepoints for underlying JDBC Connections.
566: *
567: * @see PersistenceManagerHolder
568: */
569: private static class JdoTransactionObject extends
570: JdbcTransactionObjectSupport {
571:
572: private PersistenceManagerHolder persistenceManagerHolder;
573:
574: private boolean newPersistenceManagerHolder;
575:
576: private Object transactionData;
577:
578: public void setPersistenceManagerHolder(
579: PersistenceManagerHolder persistenceManagerHolder,
580: boolean newPersistenceManagerHolder) {
581: this .persistenceManagerHolder = persistenceManagerHolder;
582: this .newPersistenceManagerHolder = newPersistenceManagerHolder;
583: }
584:
585: public PersistenceManagerHolder getPersistenceManagerHolder() {
586: return persistenceManagerHolder;
587: }
588:
589: public boolean isNewPersistenceManagerHolder() {
590: return newPersistenceManagerHolder;
591: }
592:
593: public boolean hasTransaction() {
594: return (this .persistenceManagerHolder != null
595: && this .persistenceManagerHolder
596: .getPersistenceManager() != null && this .persistenceManagerHolder
597: .getPersistenceManager().currentTransaction()
598: .isActive());
599: }
600:
601: public void setTransactionData(Object transactionData) {
602: this .transactionData = transactionData;
603: }
604:
605: public Object getTransactionData() {
606: return transactionData;
607: }
608:
609: public void setRollbackOnly() {
610: if (jdoSetRollbackOnlyAvailable) {
611: Transaction tx = this .persistenceManagerHolder
612: .getPersistenceManager().currentTransaction();
613: if (tx.isActive()) {
614: tx.setRollbackOnly();
615: }
616: } else {
617: getPersistenceManagerHolder().setRollbackOnly();
618: }
619: if (hasConnectionHolder()) {
620: getConnectionHolder().setRollbackOnly();
621: }
622: }
623:
624: public boolean isRollbackOnly() {
625: if (jdoSetRollbackOnlyAvailable) {
626: Transaction tx = this .persistenceManagerHolder
627: .getPersistenceManager().currentTransaction();
628: return tx.getRollbackOnly();
629: } else {
630: return getPersistenceManagerHolder().isRollbackOnly();
631: }
632: }
633: }
634:
635: /**
636: * Holder for suspended resources.
637: * Used internally by <code>doSuspend</code> and <code>doResume</code>.
638: */
639: private static class SuspendedResourcesHolder {
640:
641: private final PersistenceManagerHolder persistenceManagerHolder;
642:
643: private final ConnectionHolder connectionHolder;
644:
645: private SuspendedResourcesHolder(
646: PersistenceManagerHolder pmHolder,
647: ConnectionHolder conHolder) {
648: this .persistenceManagerHolder = pmHolder;
649: this .connectionHolder = conHolder;
650: }
651:
652: private PersistenceManagerHolder getPersistenceManagerHolder() {
653: return this .persistenceManagerHolder;
654: }
655:
656: private ConnectionHolder getConnectionHolder() {
657: return this.connectionHolder;
658: }
659: }
660:
661: }
|