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 PersistenceManagerFactory persistenceManagerFactory;
106:
107: private DataSource dataSource;
108:
109: private boolean autodetectDataSource = true;
110:
111: private JdoDialect jdoDialect;
112:
113: /**
114: * Create a new JdoTransactionManager instance.
115: * A PersistenceManagerFactory has to be set to be able to use it.
116: * @see #setPersistenceManagerFactory
117: */
118: public JdoTransactionManager() {
119: }
120:
121: /**
122: * Create a new JdoTransactionManager instance.
123: * @param pmf PersistenceManagerFactory to manage transactions for
124: */
125: public JdoTransactionManager(PersistenceManagerFactory pmf) {
126: this .persistenceManagerFactory = pmf;
127: afterPropertiesSet();
128: }
129:
130: /**
131: * Set the PersistenceManagerFactory that this instance should manage transactions for.
132: * <p>The PersistenceManagerFactory specified here should be the target
133: * PersistenceManagerFactory to manage transactions for, not a
134: * TransactionAwarePersistenceManagerFactoryProxy. Only data access
135: * code may work with TransactionAwarePersistenceManagerFactoryProxy, while the
136: * transaction manager needs to work on the underlying target PersistenceManagerFactory.
137: * @see TransactionAwarePersistenceManagerFactoryProxy
138: */
139: public void setPersistenceManagerFactory(
140: PersistenceManagerFactory pmf) {
141: this .persistenceManagerFactory = pmf;
142: }
143:
144: /**
145: * Return the PersistenceManagerFactory that this instance should manage transactions for.
146: */
147: public PersistenceManagerFactory getPersistenceManagerFactory() {
148: return this .persistenceManagerFactory;
149: }
150:
151: /**
152: * Set the JDBC DataSource that this instance should manage transactions for.
153: * The DataSource should match the one used by the JDO PersistenceManagerFactory:
154: * for example, you could specify the same JNDI DataSource for both.
155: * <p>If the PersistenceManagerFactory uses a DataSource as connection factory,
156: * the DataSource will be autodetected: You can still explictly specify the
157: * DataSource, but you don't need to in this case.
158: * <p>A transactional JDBC Connection for this DataSource will be provided to
159: * application code accessing this DataSource directly via DataSourceUtils
160: * or JdbcTemplate. The Connection will be taken from the JDO PersistenceManager.
161: * <p>Note that you need to use a JDO dialect for a specific JDO provider to
162: * allow for exposing JDO transactions as JDBC transactions.
163: * <p>The DataSource specified here should be the target DataSource to manage
164: * transactions for, not a TransactionAwareDataSourceProxy. Only data access
165: * code may work with TransactionAwareDataSourceProxy, while the transaction
166: * manager needs to work on the underlying target DataSource. If there's
167: * nevertheless a TransactionAwareDataSourceProxy passed in, it will be
168: * unwrapped to extract its target DataSource.
169: * @see #setAutodetectDataSource
170: * @see #setJdoDialect
171: * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory
172: * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
173: * @see org.springframework.jdbc.datasource.DataSourceUtils
174: * @see org.springframework.jdbc.core.JdbcTemplate
175: */
176: public void setDataSource(DataSource dataSource) {
177: if (dataSource instanceof TransactionAwareDataSourceProxy) {
178: // If we got a TransactionAwareDataSourceProxy, we need to perform transactions
179: // for its underlying target DataSource, else data access code won't see
180: // properly exposed transactions (i.e. transactions for the target DataSource).
181: this .dataSource = ((TransactionAwareDataSourceProxy) dataSource)
182: .getTargetDataSource();
183: } else {
184: this .dataSource = dataSource;
185: }
186: }
187:
188: /**
189: * Return the JDBC DataSource that this instance manages transactions for.
190: */
191: public DataSource getDataSource() {
192: return this .dataSource;
193: }
194:
195: /**
196: * Set whether to autodetect a JDBC DataSource used by the JDO PersistenceManagerFactory,
197: * as returned by the <code>getConnectionFactory()</code> method. Default is "true".
198: * <p>Can be turned off to deliberately ignore an available DataSource,
199: * to not expose JDO transactions as JDBC transactions for that DataSource.
200: * @see #setDataSource
201: * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory
202: */
203: public void setAutodetectDataSource(boolean autodetectDataSource) {
204: this .autodetectDataSource = autodetectDataSource;
205: }
206:
207: /**
208: * Set the JDO dialect to use for this transaction manager.
209: * <p>The dialect object can be used to retrieve the underlying JDBC connection
210: * and thus allows for exposing JDO transactions as JDBC transactions.
211: * @see JdoDialect#getJdbcConnection
212: */
213: public void setJdoDialect(JdoDialect jdoDialect) {
214: this .jdoDialect = jdoDialect;
215: }
216:
217: /**
218: * Return the JDO dialect to use for this transaction manager.
219: * <p>Creates a default one for the specified PersistenceManagerFactory if none set.
220: */
221: public JdoDialect getJdoDialect() {
222: if (this .jdoDialect == null) {
223: this .jdoDialect = new DefaultJdoDialect();
224: }
225: return this .jdoDialect;
226: }
227:
228: /**
229: * Eagerly initialize the JDO dialect, creating a default one
230: * for the specified PersistenceManagerFactory if none set.
231: * Auto-detect the PersistenceManagerFactory's DataSource, if any.
232: */
233: public void afterPropertiesSet() {
234: if (getPersistenceManagerFactory() == null) {
235: throw new IllegalArgumentException(
236: "Property 'persistenceManagerFactory' is required");
237: }
238: // Build default JdoDialect if none explicitly specified.
239: if (this .jdoDialect == null) {
240: this .jdoDialect = new DefaultJdoDialect(
241: getPersistenceManagerFactory()
242: .getConnectionFactory());
243: }
244:
245: // Check for DataSource as connection factory.
246: if (this .autodetectDataSource && getDataSource() == null) {
247: Object pmfcf = getPersistenceManagerFactory()
248: .getConnectionFactory();
249: if (pmfcf instanceof DataSource) {
250: // Use the PersistenceManagerFactory's DataSource for exposing transactions to JDBC code.
251: this .dataSource = (DataSource) pmfcf;
252: if (logger.isInfoEnabled()) {
253: logger
254: .info("Using DataSource ["
255: + this .dataSource
256: + "] of JDO PersistenceManagerFactory for JdoTransactionManager");
257: }
258: }
259: }
260: }
261:
262: public Object getResourceFactory() {
263: return getPersistenceManagerFactory();
264: }
265:
266: protected Object doGetTransaction() {
267: JdoTransactionObject txObject = new JdoTransactionObject();
268: txObject.setSavepointAllowed(isNestedTransactionAllowed());
269:
270: PersistenceManagerHolder pmHolder = (PersistenceManagerHolder) TransactionSynchronizationManager
271: .getResource(getPersistenceManagerFactory());
272: if (pmHolder != null) {
273: if (logger.isDebugEnabled()) {
274: logger.debug("Found thread-bound PersistenceManager ["
275: + pmHolder.getPersistenceManager()
276: + "] for JDO transaction");
277: }
278: txObject.setPersistenceManagerHolder(pmHolder, false);
279: }
280:
281: if (getDataSource() != null) {
282: ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager
283: .getResource(getDataSource());
284: txObject.setConnectionHolder(conHolder);
285: }
286:
287: return txObject;
288: }
289:
290: protected boolean isExistingTransaction(Object transaction) {
291: return ((JdoTransactionObject) transaction).hasTransaction();
292: }
293:
294: protected void doBegin(Object transaction,
295: TransactionDefinition definition) {
296: JdoTransactionObject txObject = (JdoTransactionObject) transaction;
297:
298: if (txObject.hasConnectionHolder()
299: && !txObject.getConnectionHolder()
300: .isSynchronizedWithTransaction()) {
301: throw new IllegalTransactionStateException(
302: "Pre-bound JDBC Connection found! JdoTransactionManager does not support "
303: + "running within DataSourceTransactionManager if told to manage the DataSource itself. "
304: + "It is recommended to use a single JdoTransactionManager for all transactions "
305: + "on a single DataSource, no matter whether JDO or JDBC access.");
306: }
307:
308: PersistenceManager pm = null;
309:
310: try {
311: if (txObject.getPersistenceManagerHolder() == null
312: || txObject.getPersistenceManagerHolder()
313: .isSynchronizedWithTransaction()) {
314: PersistenceManager newPm = getPersistenceManagerFactory()
315: .getPersistenceManager();
316: if (logger.isDebugEnabled()) {
317: logger.debug("Opened new PersistenceManager ["
318: + newPm + "] for JDO transaction");
319: }
320: txObject.setPersistenceManagerHolder(
321: new PersistenceManagerHolder(newPm), true);
322: }
323:
324: pm = txObject.getPersistenceManagerHolder()
325: .getPersistenceManager();
326:
327: // Delegate to JdoDialect for actual transaction begin.
328: Object transactionData = getJdoDialect().beginTransaction(
329: pm.currentTransaction(), definition);
330: txObject.setTransactionData(transactionData);
331:
332: // Register transaction timeout.
333: int timeout = determineTimeout(definition);
334: if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
335: txObject.getPersistenceManagerHolder()
336: .setTimeoutInSeconds(timeout);
337: }
338:
339: // Register the JDO PersistenceManager's JDBC Connection for the DataSource, if set.
340: if (getDataSource() != null) {
341: ConnectionHandle conHandle = getJdoDialect()
342: .getJdbcConnection(pm, definition.isReadOnly());
343: if (conHandle != null) {
344: ConnectionHolder conHolder = new ConnectionHolder(
345: conHandle);
346: if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
347: conHolder.setTimeoutInSeconds(timeout);
348: }
349: if (logger.isDebugEnabled()) {
350: logger
351: .debug("Exposing JDO transaction as JDBC transaction ["
352: + conHolder
353: .getConnectionHandle()
354: + "]");
355: }
356: TransactionSynchronizationManager.bindResource(
357: getDataSource(), conHolder);
358: txObject.setConnectionHolder(conHolder);
359: } else {
360: if (logger.isDebugEnabled()) {
361: logger
362: .debug("Not exposing JDO transaction ["
363: + pm
364: + "] as JDBC transaction because JdoDialect ["
365: + getJdoDialect()
366: + "] does not support JDBC Connection retrieval");
367: }
368: }
369: }
370:
371: // Bind the persistence manager holder to the thread.
372: if (txObject.isNewPersistenceManagerHolder()) {
373: TransactionSynchronizationManager.bindResource(
374: getPersistenceManagerFactory(), txObject
375: .getPersistenceManagerHolder());
376: }
377: txObject.getPersistenceManagerHolder()
378: .setSynchronizedWithTransaction(true);
379: }
380:
381: catch (TransactionException ex) {
382: PersistenceManagerFactoryUtils.releasePersistenceManager(
383: pm, getPersistenceManagerFactory());
384: throw ex;
385: } catch (Exception ex) {
386: PersistenceManagerFactoryUtils.releasePersistenceManager(
387: pm, getPersistenceManagerFactory());
388: throw new CannotCreateTransactionException(
389: "Could not open JDO PersistenceManager for transaction",
390: ex);
391: }
392: }
393:
394: protected Object doSuspend(Object transaction) {
395: JdoTransactionObject txObject = (JdoTransactionObject) transaction;
396: txObject.setPersistenceManagerHolder(null, false);
397: PersistenceManagerHolder persistenceManagerHolder = (PersistenceManagerHolder) TransactionSynchronizationManager
398: .unbindResource(getPersistenceManagerFactory());
399: txObject.setConnectionHolder(null);
400: ConnectionHolder connectionHolder = null;
401: if (getDataSource() != null
402: && TransactionSynchronizationManager
403: .hasResource(getDataSource())) {
404: connectionHolder = (ConnectionHolder) TransactionSynchronizationManager
405: .unbindResource(getDataSource());
406: }
407: return new SuspendedResourcesHolder(persistenceManagerHolder,
408: connectionHolder);
409: }
410:
411: protected void doResume(Object transaction,
412: Object suspendedResources) {
413: SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
414: TransactionSynchronizationManager.bindResource(
415: getPersistenceManagerFactory(), resourcesHolder
416: .getPersistenceManagerHolder());
417: if (getDataSource() != null
418: && resourcesHolder.getConnectionHolder() != null) {
419: TransactionSynchronizationManager.bindResource(
420: getDataSource(), resourcesHolder
421: .getConnectionHolder());
422: }
423: }
424:
425: /**
426: * This implementation returns "true": a JDO2 commit will properly handle
427: * transactions that have been marked rollback-only at a global level.
428: */
429: protected boolean shouldCommitOnGlobalRollbackOnly() {
430: return true;
431: }
432:
433: protected void doCommit(DefaultTransactionStatus status) {
434: JdoTransactionObject txObject = (JdoTransactionObject) status
435: .getTransaction();
436: if (status.isDebug()) {
437: logger
438: .debug("Committing JDO transaction on PersistenceManager ["
439: + txObject.getPersistenceManagerHolder()
440: .getPersistenceManager() + "]");
441: }
442: try {
443: Transaction tx = txObject.getPersistenceManagerHolder()
444: .getPersistenceManager().currentTransaction();
445: tx.commit();
446: } catch (JDOException ex) {
447: // Assumably failed to flush changes to database.
448: throw convertJdoAccessException(ex);
449: }
450: }
451:
452: protected void doRollback(DefaultTransactionStatus status) {
453: JdoTransactionObject txObject = (JdoTransactionObject) status
454: .getTransaction();
455: if (status.isDebug()) {
456: logger
457: .debug("Rolling back JDO transaction on PersistenceManager ["
458: + txObject.getPersistenceManagerHolder()
459: .getPersistenceManager() + "]");
460: }
461: try {
462: Transaction tx = txObject.getPersistenceManagerHolder()
463: .getPersistenceManager().currentTransaction();
464: if (tx.isActive()) {
465: tx.rollback();
466: }
467: } catch (JDOException ex) {
468: throw new TransactionSystemException(
469: "Could not roll back JDO transaction", ex);
470: }
471: }
472:
473: protected void doSetRollbackOnly(DefaultTransactionStatus status) {
474: JdoTransactionObject txObject = (JdoTransactionObject) status
475: .getTransaction();
476: if (status.isDebug()) {
477: logger
478: .debug("Setting JDO transaction on PersistenceManager ["
479: + txObject.getPersistenceManagerHolder()
480: .getPersistenceManager()
481: + "] rollback-only");
482: }
483: txObject.setRollbackOnly();
484: }
485:
486: protected void doCleanupAfterCompletion(Object transaction) {
487: JdoTransactionObject txObject = (JdoTransactionObject) transaction;
488:
489: // Remove the persistence manager holder from the thread.
490: if (txObject.isNewPersistenceManagerHolder()) {
491: TransactionSynchronizationManager
492: .unbindResource(getPersistenceManagerFactory());
493: }
494: txObject.getPersistenceManagerHolder().clear();
495:
496: // Remove the JDBC connection holder from the thread, if exposed.
497: if (txObject.hasConnectionHolder()) {
498: TransactionSynchronizationManager
499: .unbindResource(getDataSource());
500: try {
501: getJdoDialect().releaseJdbcConnection(
502: txObject.getConnectionHolder()
503: .getConnectionHandle(),
504: txObject.getPersistenceManagerHolder()
505: .getPersistenceManager());
506: } catch (Throwable ex) {
507: // Just log it, to keep a transaction-related exception.
508: logger
509: .debug(
510: "Could not release JDBC connection after transaction",
511: ex);
512: }
513: }
514:
515: getJdoDialect().cleanupTransaction(
516: txObject.getTransactionData());
517:
518: if (txObject.isNewPersistenceManagerHolder()) {
519: PersistenceManager pm = txObject
520: .getPersistenceManagerHolder()
521: .getPersistenceManager();
522: if (logger.isDebugEnabled()) {
523: logger.debug("Closing JDO PersistenceManager [" + pm
524: + "] after transaction");
525: }
526: PersistenceManagerFactoryUtils.releasePersistenceManager(
527: pm, getPersistenceManagerFactory());
528: } else {
529: logger
530: .debug("Not closing pre-bound JDO PersistenceManager after transaction");
531: }
532: }
533:
534: /**
535: * Convert the given JDOException to an appropriate exception from the
536: * <code>org.springframework.dao</code> hierarchy.
537: * <p>Default implementation delegates to the JdoDialect.
538: * May be overridden in subclasses.
539: * @param ex JDOException that occured
540: * @return the corresponding DataAccessException instance
541: * @see JdoDialect#translateException
542: */
543: protected DataAccessException convertJdoAccessException(
544: JDOException ex) {
545: return getJdoDialect().translateException(ex);
546: }
547:
548: /**
549: * JDO transaction object, representing a PersistenceManagerHolder.
550: * Used as transaction object by JdoTransactionManager.
551: */
552: private static class JdoTransactionObject extends
553: JdbcTransactionObjectSupport {
554:
555: private PersistenceManagerHolder persistenceManagerHolder;
556:
557: private boolean newPersistenceManagerHolder;
558:
559: private Object transactionData;
560:
561: public void setPersistenceManagerHolder(
562: PersistenceManagerHolder persistenceManagerHolder,
563: boolean newPersistenceManagerHolder) {
564: this .persistenceManagerHolder = persistenceManagerHolder;
565: this .newPersistenceManagerHolder = newPersistenceManagerHolder;
566: }
567:
568: public PersistenceManagerHolder getPersistenceManagerHolder() {
569: return persistenceManagerHolder;
570: }
571:
572: public boolean isNewPersistenceManagerHolder() {
573: return newPersistenceManagerHolder;
574: }
575:
576: public boolean hasTransaction() {
577: return (this .persistenceManagerHolder != null
578: && this .persistenceManagerHolder
579: .getPersistenceManager() != null && (this .persistenceManagerHolder
580: .getPersistenceManager().currentTransaction()
581: .isActive() || this .persistenceManagerHolder
582: .isSynchronizedWithTransaction()));
583: }
584:
585: public void setTransactionData(Object transactionData) {
586: this .transactionData = transactionData;
587: }
588:
589: public Object getTransactionData() {
590: return transactionData;
591: }
592:
593: public void setRollbackOnly() {
594: Transaction tx = this .persistenceManagerHolder
595: .getPersistenceManager().currentTransaction();
596: if (tx.isActive()) {
597: tx.setRollbackOnly();
598: }
599: if (hasConnectionHolder()) {
600: getConnectionHolder().setRollbackOnly();
601: }
602: }
603:
604: public boolean isRollbackOnly() {
605: Transaction tx = this .persistenceManagerHolder
606: .getPersistenceManager().currentTransaction();
607: return tx.getRollbackOnly();
608: }
609: }
610:
611: /**
612: * Holder for suspended resources.
613: * Used internally by <code>doSuspend</code> and <code>doResume</code>.
614: */
615: private static class SuspendedResourcesHolder {
616:
617: private final PersistenceManagerHolder persistenceManagerHolder;
618:
619: private final ConnectionHolder connectionHolder;
620:
621: private SuspendedResourcesHolder(
622: PersistenceManagerHolder pmHolder,
623: ConnectionHolder conHolder) {
624: this .persistenceManagerHolder = pmHolder;
625: this .connectionHolder = conHolder;
626: }
627:
628: private PersistenceManagerHolder getPersistenceManagerHolder() {
629: return this .persistenceManagerHolder;
630: }
631:
632: private ConnectionHolder getConnectionHolder() {
633: return this.connectionHolder;
634: }
635: }
636:
637: }
|