1 /* 2 * Copyright 2002-2011 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.jdo; 18 19 import javax.jdo.JDODataStoreException; 20 import javax.jdo.JDOException; 21 import javax.jdo.JDOFatalDataStoreException; 22 import javax.jdo.JDOFatalUserException; 23 import javax.jdo.JDOObjectNotFoundException; 24 import javax.jdo.JDOOptimisticVerificationException; 25 import javax.jdo.JDOUserException; 26 import javax.jdo.PersistenceManager; 27 import javax.jdo.PersistenceManagerFactory; 28 import javax.jdo.Query; 29 import javax.sql.DataSource; 30 31 import org.apache.commons.logging.Log; 32 import org.apache.commons.logging.LogFactory; 33 34 import org.springframework.core.Ordered; 35 import org.springframework.dao.DataAccessException; 36 import org.springframework.dao.DataAccessResourceFailureException; 37 import org.springframework.jdbc.datasource.DataSourceUtils; 38 import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; 39 import org.springframework.jdbc.support.SQLExceptionTranslator; 40 import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator; 41 import org.springframework.transaction.support.ResourceHolderSynchronization; 42 import org.springframework.transaction.support.TransactionSynchronizationManager; 43 import org.springframework.util.Assert; 44 45 /** 46 * Helper class featuring methods for JDO PersistenceManager handling, 47 * allowing for reuse of PersistenceManager instances within transactions. 48 * Also provides support for exception translation. 49 * 50 * <p>Used internally by {@link JdoTemplate}, {@link JdoInterceptor} and 51 * {@link JdoTransactionManager}. Can also be used directly in application code. 52 * 53 * @author Juergen Hoeller 54 * @since 03.06.2003 55 * @see JdoTransactionManager 56 * @see org.springframework.transaction.jta.JtaTransactionManager 57 * @see org.springframework.transaction.support.TransactionSynchronizationManager 58 */ 59 public abstract class PersistenceManagerFactoryUtils { 60 61 /** 62 * Order value for TransactionSynchronization objects that clean up JDO 63 * PersistenceManagers. Return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100 64 * to execute PersistenceManager cleanup before JDBC Connection cleanup, if any. 65 * @see org.springframework.jdbc.datasource.DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER 66 */ 67 public static final int PERSISTENCE_MANAGER_SYNCHRONIZATION_ORDER = 68 DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100; 69 70 private static final Log logger = LogFactory.getLog(PersistenceManagerFactoryUtils.class); 71 72 73 /** 74 * Create an appropriate SQLExceptionTranslator for the given PersistenceManagerFactory. 75 * <p>If a DataSource is found, creates a SQLErrorCodeSQLExceptionTranslator for the 76 * DataSource; else, falls back to a SQLStateSQLExceptionTranslator. 77 * @param connectionFactory the connection factory of the PersistenceManagerFactory 78 * (may be <code>null</code>) 79 * @return the SQLExceptionTranslator (never <code>null</code>) 80 * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory() 81 * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator 82 * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator 83 */ newJdbcExceptionTranslator(Object connectionFactory)84 static SQLExceptionTranslator newJdbcExceptionTranslator(Object connectionFactory) { 85 // Check for PersistenceManagerFactory's DataSource. 86 if (connectionFactory instanceof DataSource) { 87 return new SQLErrorCodeSQLExceptionTranslator((DataSource) connectionFactory); 88 } 89 else { 90 return new SQLStateSQLExceptionTranslator(); 91 } 92 } 93 94 /** 95 * Obtain a JDO PersistenceManager via the given factory. Is aware of a 96 * corresponding PersistenceManager bound to the current thread, 97 * for example when using JdoTransactionManager. Will create a new 98 * PersistenceManager else, if "allowCreate" is <code>true</code>. 99 * @param pmf PersistenceManagerFactory to create the PersistenceManager with 100 * @param allowCreate if a non-transactional PersistenceManager should be created 101 * when no transactional PersistenceManager can be found for the current thread 102 * @return the PersistenceManager 103 * @throws DataAccessResourceFailureException if the PersistenceManager couldn't be obtained 104 * @throws IllegalStateException if no thread-bound PersistenceManager found and 105 * "allowCreate" is <code>false</code> 106 * @see JdoTransactionManager 107 */ getPersistenceManager(PersistenceManagerFactory pmf, boolean allowCreate)108 public static PersistenceManager getPersistenceManager(PersistenceManagerFactory pmf, boolean allowCreate) 109 throws DataAccessResourceFailureException, IllegalStateException { 110 111 try { 112 return doGetPersistenceManager(pmf, allowCreate); 113 } 114 catch (JDOException ex) { 115 throw new DataAccessResourceFailureException("Could not obtain JDO PersistenceManager", ex); 116 } 117 } 118 119 /** 120 * Obtain a JDO PersistenceManager via the given factory. Is aware of a 121 * corresponding PersistenceManager bound to the current thread, 122 * for example when using JdoTransactionManager. Will create a new 123 * PersistenceManager else, if "allowCreate" is <code>true</code>. 124 * <p>Same as <code>getPersistenceManager</code>, but throwing the original JDOException. 125 * @param pmf PersistenceManagerFactory to create the PersistenceManager with 126 * @param allowCreate if a non-transactional PersistenceManager should be created 127 * when no transactional PersistenceManager can be found for the current thread 128 * @return the PersistenceManager 129 * @throws JDOException if the PersistenceManager couldn't be created 130 * @throws IllegalStateException if no thread-bound PersistenceManager found and 131 * "allowCreate" is <code>false</code> 132 * @see #getPersistenceManager(javax.jdo.PersistenceManagerFactory, boolean) 133 * @see JdoTransactionManager 134 */ doGetPersistenceManager(PersistenceManagerFactory pmf, boolean allowCreate)135 public static PersistenceManager doGetPersistenceManager(PersistenceManagerFactory pmf, boolean allowCreate) 136 throws JDOException, IllegalStateException { 137 138 Assert.notNull(pmf, "No PersistenceManagerFactory specified"); 139 140 PersistenceManagerHolder pmHolder = 141 (PersistenceManagerHolder) TransactionSynchronizationManager.getResource(pmf); 142 if (pmHolder != null) { 143 if (!pmHolder.isSynchronizedWithTransaction() && 144 TransactionSynchronizationManager.isSynchronizationActive()) { 145 pmHolder.setSynchronizedWithTransaction(true); 146 TransactionSynchronizationManager.registerSynchronization( 147 new PersistenceManagerSynchronization(pmHolder, pmf, false)); 148 } 149 return pmHolder.getPersistenceManager(); 150 } 151 152 if (!allowCreate && !TransactionSynchronizationManager.isSynchronizationActive()) { 153 throw new IllegalStateException("No JDO PersistenceManager bound to thread, " + 154 "and configuration does not allow creation of non-transactional one here"); 155 } 156 157 logger.debug("Opening JDO PersistenceManager"); 158 PersistenceManager pm = pmf.getPersistenceManager(); 159 160 if (TransactionSynchronizationManager.isSynchronizationActive()) { 161 logger.debug("Registering transaction synchronization for JDO PersistenceManager"); 162 // Use same PersistenceManager for further JDO actions within the transaction. 163 // Thread object will get removed by synchronization at transaction completion. 164 pmHolder = new PersistenceManagerHolder(pm); 165 pmHolder.setSynchronizedWithTransaction(true); 166 TransactionSynchronizationManager.registerSynchronization( 167 new PersistenceManagerSynchronization(pmHolder, pmf, true)); 168 TransactionSynchronizationManager.bindResource(pmf, pmHolder); 169 } 170 171 return pm; 172 } 173 174 /** 175 * Return whether the given JDO PersistenceManager is transactional, that is, 176 * bound to the current thread by Spring's transaction facilities. 177 * @param pm the JDO PersistenceManager to check 178 * @param pmf JDO PersistenceManagerFactory that the PersistenceManager 179 * was created with (can be <code>null</code>) 180 * @return whether the PersistenceManager is transactional 181 */ isPersistenceManagerTransactional( PersistenceManager pm, PersistenceManagerFactory pmf)182 public static boolean isPersistenceManagerTransactional( 183 PersistenceManager pm, PersistenceManagerFactory pmf) { 184 185 if (pmf == null) { 186 return false; 187 } 188 PersistenceManagerHolder pmHolder = 189 (PersistenceManagerHolder) TransactionSynchronizationManager.getResource(pmf); 190 return (pmHolder != null && pm == pmHolder.getPersistenceManager()); 191 } 192 193 /** 194 * Apply the current transaction timeout, if any, to the given JDO Query object. 195 * @param query the JDO Query object 196 * @param pmf JDO PersistenceManagerFactory that the Query was created for 197 * @param jdoDialect the JdoDialect to use for applying a query timeout 198 * (must not be <code>null</code>) 199 * @throws JDOException if thrown by JDO methods 200 * @see JdoDialect#applyQueryTimeout 201 */ applyTransactionTimeout( Query query, PersistenceManagerFactory pmf, JdoDialect jdoDialect)202 public static void applyTransactionTimeout( 203 Query query, PersistenceManagerFactory pmf, JdoDialect jdoDialect) throws JDOException { 204 205 Assert.notNull(query, "No Query object specified"); 206 PersistenceManagerHolder pmHolder = 207 (PersistenceManagerHolder) TransactionSynchronizationManager.getResource(pmf); 208 if (pmHolder != null && pmHolder.hasTimeout()) { 209 jdoDialect.applyQueryTimeout(query, pmHolder.getTimeToLiveInSeconds()); 210 } 211 } 212 213 /** 214 * Convert the given JDOException to an appropriate exception from the 215 * <code>org.springframework.dao</code> hierarchy. 216 * <p>The most important cases like object not found or optimistic locking 217 * failure are covered here. For more fine-granular conversion, JdoAccessor and 218 * JdoTransactionManager support sophisticated translation of exceptions via a 219 * JdoDialect. 220 * @param ex JDOException that occured 221 * @return the corresponding DataAccessException instance 222 * @see JdoAccessor#convertJdoAccessException 223 * @see JdoTransactionManager#convertJdoAccessException 224 * @see JdoDialect#translateException 225 */ convertJdoAccessException(JDOException ex)226 public static DataAccessException convertJdoAccessException(JDOException ex) { 227 if (ex instanceof JDOObjectNotFoundException) { 228 throw new JdoObjectRetrievalFailureException((JDOObjectNotFoundException) ex); 229 } 230 if (ex instanceof JDOOptimisticVerificationException) { 231 throw new JdoOptimisticLockingFailureException((JDOOptimisticVerificationException) ex); 232 } 233 if (ex instanceof JDODataStoreException) { 234 return new JdoResourceFailureException((JDODataStoreException) ex); 235 } 236 if (ex instanceof JDOFatalDataStoreException) { 237 return new JdoResourceFailureException((JDOFatalDataStoreException) ex); 238 } 239 if (ex instanceof JDOUserException) { 240 return new JdoUsageException((JDOUserException) ex); 241 } 242 if (ex instanceof JDOFatalUserException) { 243 return new JdoUsageException((JDOFatalUserException) ex); 244 } 245 // fallback 246 return new JdoSystemException(ex); 247 } 248 249 /** 250 * Close the given PersistenceManager, created via the given factory, 251 * if it is not managed externally (i.e. not bound to the thread). 252 * @param pm PersistenceManager to close 253 * @param pmf PersistenceManagerFactory that the PersistenceManager was created with 254 * (can be <code>null</code>) 255 */ releasePersistenceManager(PersistenceManager pm, PersistenceManagerFactory pmf)256 public static void releasePersistenceManager(PersistenceManager pm, PersistenceManagerFactory pmf) { 257 try { 258 doReleasePersistenceManager(pm, pmf); 259 } 260 catch (JDOException ex) { 261 logger.debug("Could not close JDO PersistenceManager", ex); 262 } 263 catch (Throwable ex) { 264 logger.debug("Unexpected exception on closing JDO PersistenceManager", ex); 265 } 266 } 267 268 /** 269 * Actually release a PersistenceManager for the given factory. 270 * Same as <code>releasePersistenceManager</code>, but throwing the original JDOException. 271 * @param pm PersistenceManager to close 272 * @param pmf PersistenceManagerFactory that the PersistenceManager was created with 273 * (can be <code>null</code>) 274 * @throws JDOException if thrown by JDO methods 275 */ doReleasePersistenceManager(PersistenceManager pm, PersistenceManagerFactory pmf)276 public static void doReleasePersistenceManager(PersistenceManager pm, PersistenceManagerFactory pmf) 277 throws JDOException { 278 279 if (pm == null) { 280 return; 281 } 282 // Only release non-transactional PersistenceManagers. 283 if (!isPersistenceManagerTransactional(pm, pmf)) { 284 logger.debug("Closing JDO PersistenceManager"); 285 pm.close(); 286 } 287 } 288 289 290 /** 291 * Callback for resource cleanup at the end of a non-JDO transaction 292 * (e.g. when participating in a JtaTransactionManager transaction). 293 * @see org.springframework.transaction.jta.JtaTransactionManager 294 */ 295 private static class PersistenceManagerSynchronization 296 extends ResourceHolderSynchronization<PersistenceManagerHolder, PersistenceManagerFactory> 297 implements Ordered { 298 299 private final boolean newPersistenceManager; 300 PersistenceManagerSynchronization( PersistenceManagerHolder pmHolder, PersistenceManagerFactory pmf, boolean newPersistenceManager)301 public PersistenceManagerSynchronization( 302 PersistenceManagerHolder pmHolder, PersistenceManagerFactory pmf, boolean newPersistenceManager) { 303 super(pmHolder, pmf); 304 this.newPersistenceManager = newPersistenceManager; 305 } 306 getOrder()307 public int getOrder() { 308 return PERSISTENCE_MANAGER_SYNCHRONIZATION_ORDER; 309 } 310 311 @Override flushResource(PersistenceManagerHolder resourceHolder)312 public void flushResource(PersistenceManagerHolder resourceHolder) { 313 try { 314 resourceHolder.getPersistenceManager().flush(); 315 } 316 catch (JDOException ex) { 317 throw convertJdoAccessException(ex); 318 } 319 } 320 321 @Override shouldUnbindAtCompletion()322 protected boolean shouldUnbindAtCompletion() { 323 return this.newPersistenceManager; 324 } 325 326 @Override shouldReleaseAfterCompletion(PersistenceManagerHolder resourceHolder)327 protected boolean shouldReleaseAfterCompletion(PersistenceManagerHolder resourceHolder) { 328 return !resourceHolder.getPersistenceManager().isClosed(); 329 } 330 331 @Override releaseResource(PersistenceManagerHolder resourceHolder, PersistenceManagerFactory resourceKey)332 protected void releaseResource(PersistenceManagerHolder resourceHolder, PersistenceManagerFactory resourceKey) { 333 releasePersistenceManager(resourceHolder.getPersistenceManager(), resourceKey); 334 } 335 } 336 337 } 338