1 16 17 package org.springframework.orm.jdo; 18 19 import javax.jdo.JDOException; 20 import javax.jdo.PersistenceManager; 21 import javax.jdo.PersistenceManagerFactory; 22 import javax.jdo.Transaction; 23 import javax.sql.DataSource ; 24 25 import org.springframework.beans.factory.InitializingBean; 26 import org.springframework.dao.DataAccessException; 27 import org.springframework.jdbc.datasource.ConnectionHandle; 28 import org.springframework.jdbc.datasource.ConnectionHolder; 29 import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport; 30 import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; 31 import org.springframework.transaction.CannotCreateTransactionException; 32 import org.springframework.transaction.IllegalTransactionStateException; 33 import org.springframework.transaction.TransactionDefinition; 34 import org.springframework.transaction.TransactionException; 35 import org.springframework.transaction.TransactionSystemException; 36 import org.springframework.transaction.support.AbstractPlatformTransactionManager; 37 import org.springframework.transaction.support.DefaultTransactionStatus; 38 import org.springframework.transaction.support.ResourceTransactionManager; 39 import org.springframework.transaction.support.TransactionSynchronizationManager; 40 41 101 public class JdoTransactionManager extends AbstractPlatformTransactionManager 102 implements ResourceTransactionManager, InitializingBean { 103 104 private static boolean jdoSetRollbackOnlyAvailable; 105 106 static { 107 try { 110 Transaction.class.getMethod("setRollbackOnly", new Class [0]); 111 jdoSetRollbackOnlyAvailable = true; 112 } 113 catch (NoSuchMethodException ex) { 114 jdoSetRollbackOnlyAvailable = false; 115 } 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 133 public JdoTransactionManager() { 134 } 135 136 140 public JdoTransactionManager(PersistenceManagerFactory pmf) { 141 this.persistenceManagerFactory = pmf; 142 afterPropertiesSet(); 143 } 144 145 154 public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { 155 this.persistenceManagerFactory = pmf; 156 } 157 158 161 public PersistenceManagerFactory getPersistenceManagerFactory() { 162 return this.persistenceManagerFactory; 163 } 164 165 190 public void setDataSource(DataSource dataSource) { 191 if (dataSource instanceof TransactionAwareDataSourceProxy) { 192 this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource(); 196 } 197 else { 198 this.dataSource = dataSource; 199 } 200 } 201 202 205 public DataSource getDataSource() { 206 return this.dataSource; 207 } 208 209 217 public void setAutodetectDataSource(boolean autodetectDataSource) { 218 this.autodetectDataSource = autodetectDataSource; 219 } 220 221 227 public void setJdoDialect(JdoDialect jdoDialect) { 228 this.jdoDialect = jdoDialect; 229 } 230 231 235 public JdoDialect getJdoDialect() { 236 if (this.jdoDialect == null) { 237 this.jdoDialect = new DefaultJdoDialect(); 238 } 239 return this.jdoDialect; 240 } 241 242 247 public void afterPropertiesSet() { 248 if (getPersistenceManagerFactory() == null) { 249 throw new IllegalArgumentException ("Property 'persistenceManagerFactory' is required"); 250 } 251 if (this.jdoDialect == null) { 253 this.jdoDialect = new DefaultJdoDialect(getPersistenceManagerFactory().getConnectionFactory()); 254 } 255 256 if (this.autodetectDataSource && getDataSource() == null) { 258 Object pmfcf = getPersistenceManagerFactory().getConnectionFactory(); 259 if (pmfcf instanceof DataSource ) { 260 this.dataSource = (DataSource ) pmfcf; 262 if (logger.isInfoEnabled()) { 263 logger.info("Using DataSource [" + this.dataSource + 264 "] of JDO PersistenceManagerFactory for JdoTransactionManager"); 265 } 266 } 267 } 268 } 269 270 271 public Object getResourceFactory() { 272 return getPersistenceManagerFactory(); 273 } 274 275 protected Object doGetTransaction() { 276 JdoTransactionObject txObject = new JdoTransactionObject(); 277 txObject.setSavepointAllowed(isNestedTransactionAllowed()); 278 279 PersistenceManagerHolder pmHolder = (PersistenceManagerHolder) 280 TransactionSynchronizationManager.getResource(getPersistenceManagerFactory()); 281 if (pmHolder != null) { 282 if (logger.isDebugEnabled()) { 283 logger.debug("Found thread-bound PersistenceManager [" + 284 pmHolder.getPersistenceManager() + "] for JDO transaction"); 285 } 286 txObject.setPersistenceManagerHolder(pmHolder, false); 287 } 288 289 if (getDataSource() != null) { 290 ConnectionHolder conHolder = (ConnectionHolder) 291 TransactionSynchronizationManager.getResource(getDataSource()); 292 txObject.setConnectionHolder(conHolder); 293 } 294 295 return txObject; 296 } 297 298 protected boolean isExistingTransaction(Object transaction) { 299 return ((JdoTransactionObject) transaction).hasTransaction(); 300 } 301 302 protected void doBegin(Object transaction, TransactionDefinition definition) { 303 JdoTransactionObject txObject = (JdoTransactionObject) transaction; 304 305 if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) { 306 throw new IllegalTransactionStateException( 307 "Pre-bound JDBC Connection found! JdoTransactionManager does not support " + 308 "running within DataSourceTransactionManager if told to manage the DataSource itself. " + 309 "It is recommended to use a single JdoTransactionManager for all transactions " + 310 "on a single DataSource, no matter whether JDO or JDBC access."); 311 } 312 313 PersistenceManager pm = null; 314 315 try { 316 if (txObject.getPersistenceManagerHolder() == null || 317 txObject.getPersistenceManagerHolder().isSynchronizedWithTransaction()) { 318 PersistenceManager newPm = getPersistenceManagerFactory().getPersistenceManager(); 319 if (logger.isDebugEnabled()) { 320 logger.debug("Opened new PersistenceManager [" + newPm + "] for JDO transaction"); 321 } 322 txObject.setPersistenceManagerHolder(new PersistenceManagerHolder(newPm), true); 323 } 324 325 txObject.getPersistenceManagerHolder().setSynchronizedWithTransaction(true); 326 pm = txObject.getPersistenceManagerHolder().getPersistenceManager(); 327 328 Object transactionData = getJdoDialect().beginTransaction(pm.currentTransaction(), definition); 330 txObject.setTransactionData(transactionData); 331 332 int timeout = determineTimeout(definition); 334 if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { 335 txObject.getPersistenceManagerHolder().setTimeoutInSeconds(timeout); 336 } 337 338 if (getDataSource() != null) { 340 ConnectionHandle conHandle = getJdoDialect().getJdbcConnection(pm, definition.isReadOnly()); 341 if (conHandle != null) { 342 ConnectionHolder conHolder = new ConnectionHolder(conHandle); 343 if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { 344 conHolder.setTimeoutInSeconds(timeout); 345 } 346 if (logger.isDebugEnabled()) { 347 logger.debug("Exposing JDO transaction as JDBC transaction [" + conHolder.getConnectionHandle() + "]"); 348 } 349 TransactionSynchronizationManager.bindResource(getDataSource(), conHolder); 350 txObject.setConnectionHolder(conHolder); 351 } 352 else { 353 if (logger.isDebugEnabled()) { 354 logger.debug("Not exposing JDO transaction [" + pm + "] as JDBC transaction because JdoDialect [" + 355 getJdoDialect() + "] does not support JDBC Connection retrieval"); 356 } 357 } 358 } 359 360 if (txObject.isNewPersistenceManagerHolder()) { 362 TransactionSynchronizationManager.bindResource( 363 getPersistenceManagerFactory(), txObject.getPersistenceManagerHolder()); 364 } 365 } 366 367 catch (TransactionException ex) { 368 PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory()); 369 throw ex; 370 } 371 catch (Exception ex) { 372 PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory()); 373 throw new CannotCreateTransactionException("Could not open JDO PersistenceManager for transaction", ex); 374 } 375 } 376 377 protected Object doSuspend(Object transaction) { 378 JdoTransactionObject txObject = (JdoTransactionObject) transaction; 379 txObject.setPersistenceManagerHolder(null, false); 380 PersistenceManagerHolder persistenceManagerHolder = (PersistenceManagerHolder) 381 TransactionSynchronizationManager.unbindResource(getPersistenceManagerFactory()); 382 txObject.setConnectionHolder(null); 383 ConnectionHolder connectionHolder = null; 384 if (getDataSource() != null) { 385 connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource()); 386 } 387 return new SuspendedResourcesHolder(persistenceManagerHolder, connectionHolder); 388 } 389 390 protected void doResume(Object transaction, Object suspendedResources) { 391 SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources; 392 TransactionSynchronizationManager.bindResource( 393 getPersistenceManagerFactory(), resourcesHolder.getPersistenceManagerHolder()); 394 if (getDataSource() != null) { 395 TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder()); 396 } 397 } 398 399 404 protected boolean shouldCommitOnGlobalRollbackOnly() { 405 return jdoSetRollbackOnlyAvailable; 406 } 407 408 protected void doCommit(DefaultTransactionStatus status) { 409 JdoTransactionObject txObject = (JdoTransactionObject) status.getTransaction(); 410 if (status.isDebug()) { 411 logger.debug("Committing JDO transaction on PersistenceManager [" + 412 txObject.getPersistenceManagerHolder().getPersistenceManager() + "]"); 413 } 414 try { 415 Transaction tx = txObject.getPersistenceManagerHolder().getPersistenceManager().currentTransaction(); 416 tx.commit(); 417 } 418 catch (JDOException ex) { 419 throw convertJdoAccessException(ex); 421 } 422 } 423 424 protected void doRollback(DefaultTransactionStatus status) { 425 JdoTransactionObject txObject = (JdoTransactionObject) status.getTransaction(); 426 if (status.isDebug()) { 427 logger.debug("Rolling back JDO transaction on PersistenceManager [" + 428 txObject.getPersistenceManagerHolder().getPersistenceManager() + "]"); 429 } 430 try { 431 Transaction tx = txObject.getPersistenceManagerHolder().getPersistenceManager().currentTransaction(); 432 if (tx.isActive()) { 433 tx.rollback(); 434 } 435 } 436 catch (JDOException ex) { 437 throw new TransactionSystemException("Could not roll back JDO transaction", ex); 438 } 439 } 440 441 protected void doSetRollbackOnly(DefaultTransactionStatus status) { 442 JdoTransactionObject txObject = (JdoTransactionObject) status.getTransaction(); 443 if (status.isDebug()) { 444 logger.debug("Setting JDO transaction on PersistenceManager [" + 445 txObject.getPersistenceManagerHolder().getPersistenceManager() + "] rollback-only"); 446 } 447 txObject.setRollbackOnly(); 448 } 449 450 protected void doCleanupAfterCompletion(Object transaction) { 451 JdoTransactionObject txObject = (JdoTransactionObject) transaction; 452 453 if (txObject.isNewPersistenceManagerHolder()) { 455 TransactionSynchronizationManager.unbindResource(getPersistenceManagerFactory()); 456 } 457 txObject.getPersistenceManagerHolder().clear(); 458 459 if (txObject.hasConnectionHolder()) { 461 TransactionSynchronizationManager.unbindResource(getDataSource()); 462 try { 463 getJdoDialect().releaseJdbcConnection(txObject.getConnectionHolder().getConnectionHandle(), 464 txObject.getPersistenceManagerHolder().getPersistenceManager()); 465 } 466 catch (Throwable ex) { 467 logger.debug("Could not release JDBC connection after transaction", ex); 469 } 470 } 471 472 getJdoDialect().cleanupTransaction(txObject.getTransactionData()); 473 474 if (txObject.isNewPersistenceManagerHolder()) { 475 PersistenceManager pm = txObject.getPersistenceManagerHolder().getPersistenceManager(); 476 if (logger.isDebugEnabled()) { 477 logger.debug("Closing JDO PersistenceManager [" + pm + "] after transaction"); 478 } 479 PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory()); 480 } 481 else { 482 logger.debug("Not closing pre-bound JDO PersistenceManager after transaction"); 483 } 484 } 485 486 495 protected DataAccessException convertJdoAccessException(JDOException ex) { 496 return getJdoDialect().translateException(ex); 497 } 498 499 500 509 private static class JdoTransactionObject extends JdbcTransactionObjectSupport { 510 511 private PersistenceManagerHolder persistenceManagerHolder; 512 513 private boolean newPersistenceManagerHolder; 514 515 private Object transactionData; 516 517 public void setPersistenceManagerHolder( 518 PersistenceManagerHolder persistenceManagerHolder, boolean newPersistenceManagerHolder) { 519 this.persistenceManagerHolder = persistenceManagerHolder; 520 this.newPersistenceManagerHolder = newPersistenceManagerHolder; 521 } 522 523 public PersistenceManagerHolder getPersistenceManagerHolder() { 524 return persistenceManagerHolder; 525 } 526 527 public boolean isNewPersistenceManagerHolder() { 528 return newPersistenceManagerHolder; 529 } 530 531 public boolean hasTransaction() { 532 return (this.persistenceManagerHolder != null && 533 this.persistenceManagerHolder.getPersistenceManager() != null && 534 this.persistenceManagerHolder.getPersistenceManager().currentTransaction().isActive()); 535 } 536 537 public void setTransactionData(Object transactionData) { 538 this.transactionData = transactionData; 539 } 540 541 public Object getTransactionData() { 542 return transactionData; 543 } 544 545 public void setRollbackOnly() { 546 if (jdoSetRollbackOnlyAvailable) { 547 Transaction tx = this.persistenceManagerHolder.getPersistenceManager().currentTransaction(); 548 if (tx.isActive()) { 549 tx.setRollbackOnly(); 550 } 551 } 552 else { 553 getPersistenceManagerHolder().setRollbackOnly(); 554 } 555 if (hasConnectionHolder()) { 556 getConnectionHolder().setRollbackOnly(); 557 } 558 } 559 560 public boolean isRollbackOnly() { 561 if (jdoSetRollbackOnlyAvailable) { 562 Transaction tx = this.persistenceManagerHolder.getPersistenceManager().currentTransaction(); 563 return tx.getRollbackOnly(); 564 } 565 else { 566 return getPersistenceManagerHolder().isRollbackOnly(); 567 } 568 } 569 } 570 571 572 576 private static class SuspendedResourcesHolder { 577 578 private final PersistenceManagerHolder persistenceManagerHolder; 579 580 private final ConnectionHolder connectionHolder; 581 582 private SuspendedResourcesHolder(PersistenceManagerHolder pmHolder, ConnectionHolder conHolder) { 583 this.persistenceManagerHolder = pmHolder; 584 this.connectionHolder = conHolder; 585 } 586 587 private PersistenceManagerHolder getPersistenceManagerHolder() { 588 return this.persistenceManagerHolder; 589 } 590 591 private ConnectionHolder getConnectionHolder() { 592 return this.connectionHolder; 593 } 594 } 595 596 } 597 | Popular Tags |