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