1. /*
  2. * Copyright 2002-2004 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. package org.springframework.orm.jdo;
  17. import javax.jdo.JDOException;
  18. import javax.jdo.JDOFatalUserException;
  19. import javax.jdo.JDOUserException;
  20. import javax.jdo.PersistenceManager;
  21. import javax.jdo.PersistenceManagerFactory;
  22. import org.apache.commons.logging.Log;
  23. import org.apache.commons.logging.LogFactory;
  24. import org.springframework.dao.CleanupFailureDataAccessException;
  25. import org.springframework.dao.DataAccessException;
  26. import org.springframework.dao.DataAccessResourceFailureException;
  27. import org.springframework.transaction.support.TransactionSynchronizationAdapter;
  28. import org.springframework.transaction.support.TransactionSynchronizationManager;
  29. /**
  30. * Helper class featuring methods for JDO PersistenceManager handling,
  31. * allowing for reuse of PersistenceManager instances within transactions.
  32. *
  33. * <p>Used by JdoTemplate, JdoInterceptor, and JdoTransactionManager.
  34. * Can also be used directly in application code, e.g. in combination
  35. * with JdoInterceptor.
  36. *
  37. * @author Juergen Hoeller
  38. * @since 03.06.2003
  39. * @see JdoTemplate
  40. * @see JdoInterceptor
  41. * @see JdoTransactionManager
  42. */
  43. public abstract class PersistenceManagerFactoryUtils {
  44. private static final Log logger = LogFactory.getLog(PersistenceManagerFactoryUtils.class);
  45. /**
  46. * Get a JDO PersistenceManager via the given factory.
  47. * Is aware of a corresponding PersistenceManager bound to the current thread,
  48. * for example when using JdoTransactionManager.
  49. * Will create a new PersistenceManager else, if allowCreate is true.
  50. * @param pmf PersistenceManagerFactory to create the session with
  51. * @param allowCreate if a new PersistenceManager should be created if no thread-bound found
  52. * @return the PersistenceManager
  53. * @throws DataAccessResourceFailureException if the PersistenceManager couldn't be created
  54. * @throws IllegalStateException if no thread-bound PersistenceManager found and allowCreate false
  55. */
  56. public static PersistenceManager getPersistenceManager(PersistenceManagerFactory pmf, boolean allowCreate)
  57. throws DataAccessResourceFailureException {
  58. return getPersistenceManager(pmf, allowCreate, true);
  59. }
  60. public static PersistenceManager getPersistenceManager(PersistenceManagerFactory pmf, boolean allowCreate,
  61. boolean allowSynchronization)
  62. throws DataAccessResourceFailureException {
  63. PersistenceManagerHolder pmHolder = (PersistenceManagerHolder) TransactionSynchronizationManager.getResource(pmf);
  64. if (pmHolder != null) {
  65. return pmHolder.getPersistenceManager();
  66. }
  67. if (!allowCreate) {
  68. throw new IllegalStateException("Not allowed to create new persistence manager");
  69. }
  70. logger.debug("Opening JDO persistence manager");
  71. try {
  72. PersistenceManager pm = pmf.getPersistenceManager();
  73. if (allowSynchronization && TransactionSynchronizationManager.isSynchronizationActive()) {
  74. logger.debug("Registering transaction synchronization for JDO persistence manager");
  75. // use same PersistenceManager for further JDO actions within the transaction
  76. // thread object will get removed by synchronization at transaction completion
  77. pmHolder = new PersistenceManagerHolder(pm);
  78. TransactionSynchronizationManager.bindResource(pmf, pmHolder);
  79. TransactionSynchronizationManager.registerSynchronization(
  80. new PersistenceManagerSynchronization(pmHolder, pmf));
  81. }
  82. return pm;
  83. }
  84. catch (JDOException ex) {
  85. throw new DataAccessResourceFailureException("Cannot get JDO persistence manager", ex);
  86. }
  87. }
  88. /**
  89. * Convert the given JDOException to an appropriate exception from the
  90. * org.springframework.dao hierarchy.
  91. * <p>Unfortunately, JDO's JDOUserException covers a lot of distinct causes
  92. * like unparsable query, optimistic locking failure, etc. Thus, we are not able
  93. * to convert to Spring's DataAccessException hierarchy in a fine-granular way
  94. * with standard JDO. JdoAccessor and JdoTransactionManager support more
  95. * sophisticated translation of exceptions via a JdoDialect.
  96. * @param ex JDOException that occured
  97. * @return the corresponding DataAccessException instance
  98. * @see JdoDialect#translateException
  99. */
  100. public static DataAccessException convertJdoAccessException(JDOException ex) {
  101. if (ex instanceof JDOUserException || ex instanceof JDOFatalUserException) {
  102. return new JdoUsageException(ex);
  103. }
  104. else {
  105. // fallback
  106. return new JdoSystemException(ex);
  107. }
  108. }
  109. /**
  110. * Close the given PersistenceManager, created via the given factory,
  111. * if it isn't bound to the thread.
  112. * @param pm PersistenceManager to close
  113. * @param pmf PersistenceManagerFactory that the PersistenceManager was created with
  114. * @throws DataAccessResourceFailureException if the PersistenceManager couldn't be closed
  115. */
  116. public static void closePersistenceManagerIfNecessary(PersistenceManager pm, PersistenceManagerFactory pmf)
  117. throws CleanupFailureDataAccessException {
  118. if (pm == null || TransactionSynchronizationManager.hasResource(pmf)) {
  119. return;
  120. }
  121. logger.debug("Closing JDO persistence manager");
  122. try {
  123. pm.close();
  124. }
  125. catch (JDOException ex) {
  126. throw new CleanupFailureDataAccessException("Cannot close JDO persistence manager", ex);
  127. }
  128. }
  129. /**
  130. * Callback for resource cleanup at the end of a non-JDO transaction
  131. * (e.g. when participating in a JTA transaction).
  132. */
  133. private static class PersistenceManagerSynchronization extends TransactionSynchronizationAdapter {
  134. private final PersistenceManagerHolder persistenceManagerHolder;
  135. private final PersistenceManagerFactory persistenceManagerFactory;
  136. private PersistenceManagerSynchronization(PersistenceManagerHolder pmHolder, PersistenceManagerFactory pmf) {
  137. this.persistenceManagerHolder = pmHolder;
  138. this.persistenceManagerFactory = pmf;
  139. }
  140. public void suspend() {
  141. TransactionSynchronizationManager.unbindResource(this.persistenceManagerFactory);
  142. }
  143. public void resume() {
  144. TransactionSynchronizationManager.bindResource(this.persistenceManagerFactory, this.persistenceManagerHolder);
  145. }
  146. public void beforeCompletion() throws CleanupFailureDataAccessException {
  147. TransactionSynchronizationManager.unbindResource(this.persistenceManagerFactory);
  148. closePersistenceManagerIfNecessary(this.persistenceManagerHolder.getPersistenceManager(),
  149. this.persistenceManagerFactory);
  150. }
  151. }
  152. }