001/* 002 * Copyright 2002-2018 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 * https://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 017package org.springframework.transaction.jta; 018 019import java.io.IOException; 020import java.io.ObjectInputStream; 021import java.io.Serializable; 022import java.util.List; 023import java.util.Properties; 024import javax.naming.NamingException; 025import javax.transaction.HeuristicMixedException; 026import javax.transaction.HeuristicRollbackException; 027import javax.transaction.InvalidTransactionException; 028import javax.transaction.NotSupportedException; 029import javax.transaction.RollbackException; 030import javax.transaction.Status; 031import javax.transaction.SystemException; 032import javax.transaction.Transaction; 033import javax.transaction.TransactionManager; 034import javax.transaction.TransactionSynchronizationRegistry; 035import javax.transaction.UserTransaction; 036 037import org.springframework.beans.factory.InitializingBean; 038import org.springframework.jndi.JndiTemplate; 039import org.springframework.transaction.CannotCreateTransactionException; 040import org.springframework.transaction.HeuristicCompletionException; 041import org.springframework.transaction.IllegalTransactionStateException; 042import org.springframework.transaction.InvalidIsolationLevelException; 043import org.springframework.transaction.NestedTransactionNotSupportedException; 044import org.springframework.transaction.TransactionDefinition; 045import org.springframework.transaction.TransactionSuspensionNotSupportedException; 046import org.springframework.transaction.TransactionSystemException; 047import org.springframework.transaction.UnexpectedRollbackException; 048import org.springframework.transaction.support.AbstractPlatformTransactionManager; 049import org.springframework.transaction.support.DefaultTransactionStatus; 050import org.springframework.transaction.support.TransactionSynchronization; 051import org.springframework.util.Assert; 052import org.springframework.util.StringUtils; 053 054/** 055 * {@link org.springframework.transaction.PlatformTransactionManager} implementation 056 * for JTA, delegating to a backend JTA provider. This is typically used to delegate 057 * to a Java EE server's transaction coordinator, but may also be configured with a 058 * local JTA provider which is embedded within the application. 059 * 060 * <p>This transaction manager is appropriate for handling distributed transactions, 061 * i.e. transactions that span multiple resources, and for controlling transactions on 062 * application server resources (e.g. JDBC DataSources available in JNDI) in general. 063 * For a single JDBC DataSource, DataSourceTransactionManager is perfectly sufficient, 064 * and for accessing a single resource with Hibernate (including transactional cache), 065 * HibernateTransactionManager is appropriate, for example. 066 * 067 * <p><b>For typical JTA transactions (REQUIRED, SUPPORTS, MANDATORY, NEVER), a plain 068 * JtaTransactionManager definition is all you need, portable across all Java EE servers.</b> 069 * This corresponds to the functionality of the JTA UserTransaction, for which Java EE 070 * specifies a standard JNDI name ("java:comp/UserTransaction"). There is no need to 071 * configure a server-specific TransactionManager lookup for this kind of JTA usage. 072 * 073 * <p><b>Transaction suspension (REQUIRES_NEW, NOT_SUPPORTED) is just available with a 074 * JTA TransactionManager being registered.</b> Common TransactionManager locations are 075 * autodetected by JtaTransactionManager, provided that the "autodetectTransactionManager" 076 * flag is set to "true" (which it is by default). 077 * 078 * <p>Note: Support for the JTA TransactionManager interface is not required by Java EE. 079 * Almost all Java EE servers expose it, but do so as extension to EE. There might be some 080 * issues with compatibility, despite the TransactionManager interface being part of JTA. 081 * As a consequence, Spring provides various vendor-specific PlatformTransactionManagers, 082 * which are recommended to be used if appropriate: {@link WebLogicJtaTransactionManager} 083 * and {@link WebSphereUowTransactionManager}. For all other Java EE servers, the 084 * standard JtaTransactionManager is sufficient. 085 * 086 * <p>This pure JtaTransactionManager class supports timeouts but not per-transaction 087 * isolation levels. Custom subclasses may override the {@link #doJtaBegin} method for 088 * specific JTA extensions in order to provide this functionality; Spring includes a 089 * corresponding {@link WebLogicJtaTransactionManager} class for WebLogic Server. Such 090 * adapters for specific Java EE transaction coordinators may also expose transaction 091 * names for monitoring; with standard JTA, transaction names will simply be ignored. 092 * 093 * <p><b>Consider using Spring's {@code tx:jta-transaction-manager} configuration 094 * element for automatically picking the appropriate JTA platform transaction manager 095 * (automatically detecting WebLogic and WebSphere).</b> 096 * 097 * <p>JTA 1.1 adds the TransactionSynchronizationRegistry facility, as public Java EE 5 098 * API in addition to the standard JTA UserTransaction handle. As of Spring 2.5, this 099 * JtaTransactionManager autodetects the TransactionSynchronizationRegistry and uses 100 * it for registering Spring-managed synchronizations when participating in an existing 101 * JTA transaction (e.g. controlled by EJB CMT). If no TransactionSynchronizationRegistry 102 * is available, then such synchronizations will be registered via the (non-EE) JTA 103 * TransactionManager handle. 104 * 105 * <p>This class is serializable. However, active synchronizations do not survive serialization. 106 * 107 * @author Juergen Hoeller 108 * @since 24.03.2003 109 * @see javax.transaction.UserTransaction 110 * @see javax.transaction.TransactionManager 111 * @see javax.transaction.TransactionSynchronizationRegistry 112 * @see #setUserTransactionName 113 * @see #setUserTransaction 114 * @see #setTransactionManagerName 115 * @see #setTransactionManager 116 * @see WebLogicJtaTransactionManager 117 */ 118@SuppressWarnings("serial") 119public class JtaTransactionManager extends AbstractPlatformTransactionManager 120 implements TransactionFactory, InitializingBean, Serializable { 121 122 /** 123 * Default JNDI location for the JTA UserTransaction. Many Java EE servers 124 * also provide support for the JTA TransactionManager interface there. 125 * @see #setUserTransactionName 126 * @see #setAutodetectTransactionManager 127 */ 128 public static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction"; 129 130 /** 131 * Fallback JNDI locations for the JTA TransactionManager. Applied if 132 * the JTA UserTransaction does not implement the JTA TransactionManager 133 * interface, provided that the "autodetectTransactionManager" flag is "true". 134 * @see #setTransactionManagerName 135 * @see #setAutodetectTransactionManager 136 */ 137 public static final String[] FALLBACK_TRANSACTION_MANAGER_NAMES = 138 new String[] {"java:comp/TransactionManager", "java:appserver/TransactionManager", 139 "java:pm/TransactionManager", "java:/TransactionManager"}; 140 141 /** 142 * Standard Java EE 5 JNDI location for the JTA TransactionSynchronizationRegistry. 143 * Autodetected when available. 144 */ 145 public static final String DEFAULT_TRANSACTION_SYNCHRONIZATION_REGISTRY_NAME = 146 "java:comp/TransactionSynchronizationRegistry"; 147 148 149 private transient JndiTemplate jndiTemplate = new JndiTemplate(); 150 151 private transient UserTransaction userTransaction; 152 153 private String userTransactionName; 154 155 private boolean autodetectUserTransaction = true; 156 157 private boolean cacheUserTransaction = true; 158 159 private boolean userTransactionObtainedFromJndi = false; 160 161 private transient TransactionManager transactionManager; 162 163 private String transactionManagerName; 164 165 private boolean autodetectTransactionManager = true; 166 167 private transient TransactionSynchronizationRegistry transactionSynchronizationRegistry; 168 169 private String transactionSynchronizationRegistryName; 170 171 private boolean autodetectTransactionSynchronizationRegistry = true; 172 173 private boolean allowCustomIsolationLevels = false; 174 175 176 /** 177 * Create a new JtaTransactionManager instance, to be configured as bean. 178 * Invoke {@code afterPropertiesSet} to activate the configuration. 179 * @see #setUserTransactionName 180 * @see #setUserTransaction 181 * @see #setTransactionManagerName 182 * @see #setTransactionManager 183 * @see #afterPropertiesSet() 184 */ 185 public JtaTransactionManager() { 186 setNestedTransactionAllowed(true); 187 } 188 189 /** 190 * Create a new JtaTransactionManager instance. 191 * @param userTransaction the JTA UserTransaction to use as direct reference 192 */ 193 public JtaTransactionManager(UserTransaction userTransaction) { 194 this(); 195 Assert.notNull(userTransaction, "UserTransaction must not be null"); 196 this.userTransaction = userTransaction; 197 } 198 199 /** 200 * Create a new JtaTransactionManager instance. 201 * @param userTransaction the JTA UserTransaction to use as direct reference 202 * @param transactionManager the JTA TransactionManager to use as direct reference 203 */ 204 public JtaTransactionManager(UserTransaction userTransaction, TransactionManager transactionManager) { 205 this(); 206 Assert.notNull(userTransaction, "UserTransaction must not be null"); 207 Assert.notNull(transactionManager, "TransactionManager must not be null"); 208 this.userTransaction = userTransaction; 209 this.transactionManager = transactionManager; 210 } 211 212 /** 213 * Create a new JtaTransactionManager instance. 214 * @param transactionManager the JTA TransactionManager to use as direct reference 215 */ 216 public JtaTransactionManager(TransactionManager transactionManager) { 217 this(); 218 Assert.notNull(transactionManager, "TransactionManager must not be null"); 219 this.transactionManager = transactionManager; 220 this.userTransaction = buildUserTransaction(transactionManager); 221 } 222 223 224 /** 225 * Set the JndiTemplate to use for JNDI lookups. 226 * A default one is used if not set. 227 */ 228 public void setJndiTemplate(JndiTemplate jndiTemplate) { 229 Assert.notNull(jndiTemplate, "JndiTemplate must not be null"); 230 this.jndiTemplate = jndiTemplate; 231 } 232 233 /** 234 * Return the JndiTemplate used for JNDI lookups. 235 */ 236 public JndiTemplate getJndiTemplate() { 237 return this.jndiTemplate; 238 } 239 240 /** 241 * Set the JNDI environment to use for JNDI lookups. 242 * Creates a JndiTemplate with the given environment settings. 243 * @see #setJndiTemplate 244 */ 245 public void setJndiEnvironment(Properties jndiEnvironment) { 246 this.jndiTemplate = new JndiTemplate(jndiEnvironment); 247 } 248 249 /** 250 * Return the JNDI environment to use for JNDI lookups. 251 */ 252 public Properties getJndiEnvironment() { 253 return this.jndiTemplate.getEnvironment(); 254 } 255 256 257 /** 258 * Set the JTA UserTransaction to use as direct reference. 259 * <p>Typically just used for local JTA setups; in a Java EE environment, 260 * the UserTransaction will always be fetched from JNDI. 261 * @see #setUserTransactionName 262 * @see #setAutodetectUserTransaction 263 */ 264 public void setUserTransaction(UserTransaction userTransaction) { 265 this.userTransaction = userTransaction; 266 } 267 268 /** 269 * Return the JTA UserTransaction that this transaction manager uses. 270 */ 271 public UserTransaction getUserTransaction() { 272 return this.userTransaction; 273 } 274 275 /** 276 * Set the JNDI name of the JTA UserTransaction. 277 * <p>Note that the UserTransaction will be autodetected at the Java EE 278 * default location "java:comp/UserTransaction" if not specified explicitly. 279 * @see #DEFAULT_USER_TRANSACTION_NAME 280 * @see #setUserTransaction 281 * @see #setAutodetectUserTransaction 282 */ 283 public void setUserTransactionName(String userTransactionName) { 284 this.userTransactionName = userTransactionName; 285 } 286 287 /** 288 * Set whether to autodetect the JTA UserTransaction at its default 289 * JNDI location "java:comp/UserTransaction", as specified by Java EE. 290 * Will proceed without UserTransaction if none found. 291 * <p>Default is "true", autodetecting the UserTransaction unless 292 * it has been specified explicitly. Turn this flag off to allow for 293 * JtaTransactionManager operating against the TransactionManager only, 294 * despite a default UserTransaction being available. 295 * @see #DEFAULT_USER_TRANSACTION_NAME 296 */ 297 public void setAutodetectUserTransaction(boolean autodetectUserTransaction) { 298 this.autodetectUserTransaction = autodetectUserTransaction; 299 } 300 301 /** 302 * Set whether to cache the JTA UserTransaction object fetched from JNDI. 303 * <p>Default is "true": UserTransaction lookup will only happen at startup, 304 * reusing the same UserTransaction handle for all transactions of all threads. 305 * This is the most efficient choice for all application servers that provide 306 * a shared UserTransaction object (the typical case). 307 * <p>Turn this flag off to enforce a fresh lookup of the UserTransaction 308 * for every transaction. This is only necessary for application servers 309 * that return a new UserTransaction for every transaction, keeping state 310 * tied to the UserTransaction object itself rather than the current thread. 311 * @see #setUserTransactionName 312 */ 313 public void setCacheUserTransaction(boolean cacheUserTransaction) { 314 this.cacheUserTransaction = cacheUserTransaction; 315 } 316 317 /** 318 * Set the JTA TransactionManager to use as direct reference. 319 * <p>A TransactionManager is necessary for suspending and resuming transactions, 320 * as this not supported by the UserTransaction interface. 321 * <p>Note that the TransactionManager will be autodetected if the JTA 322 * UserTransaction object implements the JTA TransactionManager interface too, 323 * as well as autodetected at various well-known fallback JNDI locations. 324 * @see #setTransactionManagerName 325 * @see #setAutodetectTransactionManager 326 */ 327 public void setTransactionManager(TransactionManager transactionManager) { 328 this.transactionManager = transactionManager; 329 } 330 331 /** 332 * Return the JTA TransactionManager that this transaction manager uses, if any. 333 */ 334 public TransactionManager getTransactionManager() { 335 return this.transactionManager; 336 } 337 338 /** 339 * Set the JNDI name of the JTA TransactionManager. 340 * <p>A TransactionManager is necessary for suspending and resuming transactions, 341 * as this not supported by the UserTransaction interface. 342 * <p>Note that the TransactionManager will be autodetected if the JTA 343 * UserTransaction object implements the JTA TransactionManager interface too, 344 * as well as autodetected at various well-known fallback JNDI locations. 345 * @see #setTransactionManager 346 * @see #setAutodetectTransactionManager 347 */ 348 public void setTransactionManagerName(String transactionManagerName) { 349 this.transactionManagerName = transactionManagerName; 350 } 351 352 /** 353 * Set whether to autodetect a JTA UserTransaction object that implements 354 * the JTA TransactionManager interface too (i.e. the JNDI location for the 355 * TransactionManager is "java:comp/UserTransaction", same as for the UserTransaction). 356 * Also checks the fallback JNDI locations "java:comp/TransactionManager" and 357 * "java:/TransactionManager". Will proceed without TransactionManager if none found. 358 * <p>Default is "true", autodetecting the TransactionManager unless it has been 359 * specified explicitly. Can be turned off to deliberately ignore an available 360 * TransactionManager, for example when there are known issues with suspend/resume 361 * and any attempt to use REQUIRES_NEW or NOT_SUPPORTED should fail fast. 362 * @see #FALLBACK_TRANSACTION_MANAGER_NAMES 363 */ 364 public void setAutodetectTransactionManager(boolean autodetectTransactionManager) { 365 this.autodetectTransactionManager = autodetectTransactionManager; 366 } 367 368 /** 369 * Set the JTA 1.1 TransactionSynchronizationRegistry to use as direct reference. 370 * <p>A TransactionSynchronizationRegistry allows for interposed registration 371 * of transaction synchronizations, as an alternative to the regular registration 372 * methods on the JTA TransactionManager API. Also, it is an official part of the 373 * Java EE 5 platform, in contrast to the JTA TransactionManager itself. 374 * <p>Note that the TransactionSynchronizationRegistry will be autodetected in JNDI and 375 * also from the UserTransaction/TransactionManager object if implemented there as well. 376 * @see #setTransactionSynchronizationRegistryName 377 * @see #setAutodetectTransactionSynchronizationRegistry 378 */ 379 public void setTransactionSynchronizationRegistry(TransactionSynchronizationRegistry transactionSynchronizationRegistry) { 380 this.transactionSynchronizationRegistry = transactionSynchronizationRegistry; 381 } 382 383 /** 384 * Return the JTA 1.1 TransactionSynchronizationRegistry that this transaction manager uses, if any. 385 */ 386 public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() { 387 return this.transactionSynchronizationRegistry; 388 } 389 390 /** 391 * Set the JNDI name of the JTA 1.1 TransactionSynchronizationRegistry. 392 * <p>Note that the TransactionSynchronizationRegistry will be autodetected 393 * at the Java EE 5 default location "java:comp/TransactionSynchronizationRegistry" 394 * if not specified explicitly. 395 * @see #DEFAULT_TRANSACTION_SYNCHRONIZATION_REGISTRY_NAME 396 */ 397 public void setTransactionSynchronizationRegistryName(String transactionSynchronizationRegistryName) { 398 this.transactionSynchronizationRegistryName = transactionSynchronizationRegistryName; 399 } 400 401 /** 402 * Set whether to autodetect a JTA 1.1 TransactionSynchronizationRegistry object 403 * at its default JDNI location ("java:comp/TransactionSynchronizationRegistry") 404 * if the UserTransaction has also been obtained from JNDI, and also whether 405 * to fall back to checking whether the JTA UserTransaction/TransactionManager 406 * object implements the JTA TransactionSynchronizationRegistry interface too. 407 * <p>Default is "true", autodetecting the TransactionSynchronizationRegistry 408 * unless it has been specified explicitly. Can be turned off to delegate 409 * synchronization registration to the regular JTA TransactionManager API. 410 */ 411 public void setAutodetectTransactionSynchronizationRegistry(boolean autodetectTransactionSynchronizationRegistry) { 412 this.autodetectTransactionSynchronizationRegistry = autodetectTransactionSynchronizationRegistry; 413 } 414 415 /** 416 * Set whether to allow custom isolation levels to be specified. 417 * <p>Default is "false", throwing an exception if a non-default isolation level 418 * is specified for a transaction. Turn this flag on if affected resource adapters 419 * check the thread-bound transaction context and apply the specified isolation 420 * levels individually (e.g. through an IsolationLevelDataSourceAdapter). 421 * @see org.springframework.jdbc.datasource.IsolationLevelDataSourceAdapter 422 * @see org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter 423 */ 424 public void setAllowCustomIsolationLevels(boolean allowCustomIsolationLevels) { 425 this.allowCustomIsolationLevels = allowCustomIsolationLevels; 426 } 427 428 429 /** 430 * Initialize the UserTransaction as well as the TransactionManager handle. 431 * @see #initUserTransactionAndTransactionManager() 432 */ 433 @Override 434 public void afterPropertiesSet() throws TransactionSystemException { 435 initUserTransactionAndTransactionManager(); 436 checkUserTransactionAndTransactionManager(); 437 initTransactionSynchronizationRegistry(); 438 } 439 440 /** 441 * Initialize the UserTransaction as well as the TransactionManager handle. 442 * @throws TransactionSystemException if initialization failed 443 */ 444 protected void initUserTransactionAndTransactionManager() throws TransactionSystemException { 445 if (this.userTransaction == null) { 446 // Fetch JTA UserTransaction from JNDI, if necessary. 447 if (StringUtils.hasLength(this.userTransactionName)) { 448 this.userTransaction = lookupUserTransaction(this.userTransactionName); 449 this.userTransactionObtainedFromJndi = true; 450 } 451 else { 452 this.userTransaction = retrieveUserTransaction(); 453 if (this.userTransaction == null && this.autodetectUserTransaction) { 454 // Autodetect UserTransaction at its default JNDI location. 455 this.userTransaction = findUserTransaction(); 456 } 457 } 458 } 459 460 if (this.transactionManager == null) { 461 // Fetch JTA TransactionManager from JNDI, if necessary. 462 if (StringUtils.hasLength(this.transactionManagerName)) { 463 this.transactionManager = lookupTransactionManager(this.transactionManagerName); 464 } 465 else { 466 this.transactionManager = retrieveTransactionManager(); 467 if (this.transactionManager == null && this.autodetectTransactionManager) { 468 // Autodetect UserTransaction object that implements TransactionManager, 469 // and check fallback JNDI locations otherwise. 470 this.transactionManager = findTransactionManager(this.userTransaction); 471 } 472 } 473 } 474 475 // If only JTA TransactionManager specified, create UserTransaction handle for it. 476 if (this.userTransaction == null && this.transactionManager != null) { 477 this.userTransaction = buildUserTransaction(this.transactionManager); 478 } 479 } 480 481 /** 482 * Check the UserTransaction as well as the TransactionManager handle, 483 * assuming standard JTA requirements. 484 * @throws IllegalStateException if no sufficient handles are available 485 */ 486 protected void checkUserTransactionAndTransactionManager() throws IllegalStateException { 487 // We at least need the JTA UserTransaction. 488 if (this.userTransaction != null) { 489 if (logger.isInfoEnabled()) { 490 logger.info("Using JTA UserTransaction: " + this.userTransaction); 491 } 492 } 493 else { 494 throw new IllegalStateException("No JTA UserTransaction available - specify either " + 495 "'userTransaction' or 'userTransactionName' or 'transactionManager' or 'transactionManagerName'"); 496 } 497 498 // For transaction suspension, the JTA TransactionManager is necessary too. 499 if (this.transactionManager != null) { 500 if (logger.isInfoEnabled()) { 501 logger.info("Using JTA TransactionManager: " + this.transactionManager); 502 } 503 } 504 else { 505 logger.warn("No JTA TransactionManager found: transaction suspension not available"); 506 } 507 } 508 509 /** 510 * Initialize the JTA 1.1 TransactionSynchronizationRegistry, if available. 511 * <p>To be called after {@link #initUserTransactionAndTransactionManager()}, 512 * since it may check the UserTransaction and TransactionManager handles. 513 * @throws TransactionSystemException if initialization failed 514 */ 515 protected void initTransactionSynchronizationRegistry() { 516 if (this.transactionSynchronizationRegistry == null) { 517 // Fetch JTA TransactionSynchronizationRegistry from JNDI, if necessary. 518 if (StringUtils.hasLength(this.transactionSynchronizationRegistryName)) { 519 this.transactionSynchronizationRegistry = 520 lookupTransactionSynchronizationRegistry(this.transactionSynchronizationRegistryName); 521 } 522 else { 523 this.transactionSynchronizationRegistry = retrieveTransactionSynchronizationRegistry(); 524 if (this.transactionSynchronizationRegistry == null && this.autodetectTransactionSynchronizationRegistry) { 525 // Autodetect in JNDI if applicable, and check UserTransaction/TransactionManager 526 // object that implements TransactionSynchronizationRegistry otherwise. 527 this.transactionSynchronizationRegistry = 528 findTransactionSynchronizationRegistry(this.userTransaction, this.transactionManager); 529 } 530 } 531 } 532 533 if (this.transactionSynchronizationRegistry != null) { 534 if (logger.isInfoEnabled()) { 535 logger.info("Using JTA TransactionSynchronizationRegistry: " + this.transactionSynchronizationRegistry); 536 } 537 } 538 } 539 540 541 /** 542 * Build a UserTransaction handle based on the given TransactionManager. 543 * @param transactionManager the TransactionManager 544 * @return a corresponding UserTransaction handle 545 */ 546 protected UserTransaction buildUserTransaction(TransactionManager transactionManager) { 547 if (transactionManager instanceof UserTransaction) { 548 return (UserTransaction) transactionManager; 549 } 550 else { 551 return new UserTransactionAdapter(transactionManager); 552 } 553 } 554 555 /** 556 * Look up the JTA UserTransaction in JNDI via the configured name. 557 * <p>Called by {@code afterPropertiesSet} if no direct UserTransaction reference was set. 558 * Can be overridden in subclasses to provide a different UserTransaction object. 559 * @param userTransactionName the JNDI name of the UserTransaction 560 * @return the UserTransaction object 561 * @throws TransactionSystemException if the JNDI lookup failed 562 * @see #setJndiTemplate 563 * @see #setUserTransactionName 564 */ 565 protected UserTransaction lookupUserTransaction(String userTransactionName) 566 throws TransactionSystemException { 567 try { 568 if (logger.isDebugEnabled()) { 569 logger.debug("Retrieving JTA UserTransaction from JNDI location [" + userTransactionName + "]"); 570 } 571 return getJndiTemplate().lookup(userTransactionName, UserTransaction.class); 572 } 573 catch (NamingException ex) { 574 throw new TransactionSystemException( 575 "JTA UserTransaction is not available at JNDI location [" + userTransactionName + "]", ex); 576 } 577 } 578 579 /** 580 * Look up the JTA TransactionManager in JNDI via the configured name. 581 * <p>Called by {@code afterPropertiesSet} if no direct TransactionManager reference was set. 582 * Can be overridden in subclasses to provide a different TransactionManager object. 583 * @param transactionManagerName the JNDI name of the TransactionManager 584 * @return the UserTransaction object 585 * @throws TransactionSystemException if the JNDI lookup failed 586 * @see #setJndiTemplate 587 * @see #setTransactionManagerName 588 */ 589 protected TransactionManager lookupTransactionManager(String transactionManagerName) 590 throws TransactionSystemException { 591 try { 592 if (logger.isDebugEnabled()) { 593 logger.debug("Retrieving JTA TransactionManager from JNDI location [" + transactionManagerName + "]"); 594 } 595 return getJndiTemplate().lookup(transactionManagerName, TransactionManager.class); 596 } 597 catch (NamingException ex) { 598 throw new TransactionSystemException( 599 "JTA TransactionManager is not available at JNDI location [" + transactionManagerName + "]", ex); 600 } 601 } 602 603 /** 604 * Look up the JTA 1.1 TransactionSynchronizationRegistry in JNDI via the configured name. 605 * <p>Can be overridden in subclasses to provide a different TransactionManager object. 606 * @param registryName the JNDI name of the 607 * TransactionSynchronizationRegistry 608 * @return the TransactionSynchronizationRegistry object 609 * @throws TransactionSystemException if the JNDI lookup failed 610 * @see #setJndiTemplate 611 * @see #setTransactionSynchronizationRegistryName 612 */ 613 protected TransactionSynchronizationRegistry lookupTransactionSynchronizationRegistry(String registryName) throws TransactionSystemException { 614 try { 615 if (logger.isDebugEnabled()) { 616 logger.debug("Retrieving JTA TransactionSynchronizationRegistry from JNDI location [" + registryName + "]"); 617 } 618 return getJndiTemplate().lookup(registryName, TransactionSynchronizationRegistry.class); 619 } 620 catch (NamingException ex) { 621 throw new TransactionSystemException( 622 "JTA TransactionSynchronizationRegistry is not available at JNDI location [" + registryName + "]", ex); 623 } 624 } 625 626 /** 627 * Allows subclasses to retrieve the JTA UserTransaction in a vendor-specific manner. 628 * Only called if no "userTransaction" or "userTransactionName" specified. 629 * <p>The default implementation simply returns {@code null}. 630 * @return the JTA UserTransaction handle to use, or {@code null} if none found 631 * @throws TransactionSystemException in case of errors 632 * @see #setUserTransaction 633 * @see #setUserTransactionName 634 */ 635 protected UserTransaction retrieveUserTransaction() throws TransactionSystemException { 636 return null; 637 } 638 639 /** 640 * Allows subclasses to retrieve the JTA TransactionManager in a vendor-specific manner. 641 * Only called if no "transactionManager" or "transactionManagerName" specified. 642 * <p>The default implementation simply returns {@code null}. 643 * @return the JTA TransactionManager handle to use, or {@code null} if none found 644 * @throws TransactionSystemException in case of errors 645 * @see #setTransactionManager 646 * @see #setTransactionManagerName 647 */ 648 protected TransactionManager retrieveTransactionManager() throws TransactionSystemException { 649 return null; 650 } 651 652 /** 653 * Allows subclasses to retrieve the JTA 1.1 TransactionSynchronizationRegistry 654 * in a vendor-specific manner. 655 * <p>The default implementation simply returns {@code null}. 656 * @return the JTA TransactionSynchronizationRegistry handle to use, 657 * or {@code null} if none found 658 * @throws TransactionSystemException in case of errors 659 */ 660 protected TransactionSynchronizationRegistry retrieveTransactionSynchronizationRegistry() throws TransactionSystemException { 661 return null; 662 } 663 664 /** 665 * Find the JTA UserTransaction through a default JNDI lookup: 666 * "java:comp/UserTransaction". 667 * @return the JTA UserTransaction reference, or {@code null} if not found 668 * @see #DEFAULT_USER_TRANSACTION_NAME 669 */ 670 protected UserTransaction findUserTransaction() { 671 String jndiName = DEFAULT_USER_TRANSACTION_NAME; 672 try { 673 UserTransaction ut = getJndiTemplate().lookup(jndiName, UserTransaction.class); 674 if (logger.isDebugEnabled()) { 675 logger.debug("JTA UserTransaction found at default JNDI location [" + jndiName + "]"); 676 } 677 this.userTransactionObtainedFromJndi = true; 678 return ut; 679 } 680 catch (NamingException ex) { 681 if (logger.isDebugEnabled()) { 682 logger.debug("No JTA UserTransaction found at default JNDI location [" + jndiName + "]", ex); 683 } 684 return null; 685 } 686 } 687 688 /** 689 * Find the JTA TransactionManager through autodetection: checking whether the 690 * UserTransaction object implements the TransactionManager, and checking the 691 * fallback JNDI locations. 692 * @param ut the JTA UserTransaction object 693 * @return the JTA TransactionManager reference, or {@code null} if not found 694 * @see #FALLBACK_TRANSACTION_MANAGER_NAMES 695 */ 696 protected TransactionManager findTransactionManager(UserTransaction ut) { 697 if (ut instanceof TransactionManager) { 698 if (logger.isDebugEnabled()) { 699 logger.debug("JTA UserTransaction object [" + ut + "] implements TransactionManager"); 700 } 701 return (TransactionManager) ut; 702 } 703 704 // Check fallback JNDI locations. 705 for (String jndiName : FALLBACK_TRANSACTION_MANAGER_NAMES) { 706 try { 707 TransactionManager tm = getJndiTemplate().lookup(jndiName, TransactionManager.class); 708 if (logger.isDebugEnabled()) { 709 logger.debug("JTA TransactionManager found at fallback JNDI location [" + jndiName + "]"); 710 } 711 return tm; 712 } 713 catch (NamingException ex) { 714 if (logger.isDebugEnabled()) { 715 logger.debug("No JTA TransactionManager found at fallback JNDI location [" + jndiName + "]", ex); 716 } 717 } 718 } 719 720 // OK, so no JTA TransactionManager is available... 721 return null; 722 } 723 724 /** 725 * Find the JTA 1.1 TransactionSynchronizationRegistry through autodetection: 726 * checking whether the UserTransaction object or TransactionManager object 727 * implements it, and checking Java EE 5's standard JNDI location. 728 * <p>The default implementation simply returns {@code null}. 729 * @param ut the JTA UserTransaction object 730 * @param tm the JTA TransactionManager object 731 * @return the JTA TransactionSynchronizationRegistry handle to use, 732 * or {@code null} if none found 733 * @throws TransactionSystemException in case of errors 734 */ 735 protected TransactionSynchronizationRegistry findTransactionSynchronizationRegistry(UserTransaction ut, TransactionManager tm) 736 throws TransactionSystemException { 737 738 if (this.userTransactionObtainedFromJndi) { 739 // UserTransaction has already been obtained from JNDI, so the 740 // TransactionSynchronizationRegistry probably sits there as well. 741 String jndiName = DEFAULT_TRANSACTION_SYNCHRONIZATION_REGISTRY_NAME; 742 try { 743 TransactionSynchronizationRegistry tsr = getJndiTemplate().lookup(jndiName, TransactionSynchronizationRegistry.class); 744 if (logger.isDebugEnabled()) { 745 logger.debug("JTA TransactionSynchronizationRegistry found at default JNDI location [" + jndiName + "]"); 746 } 747 return tsr; 748 } 749 catch (NamingException ex) { 750 if (logger.isDebugEnabled()) { 751 logger.debug("No JTA TransactionSynchronizationRegistry found at default JNDI location [" + jndiName + "]", ex); 752 } 753 } 754 } 755 // Check whether the UserTransaction or TransactionManager implements it... 756 if (ut instanceof TransactionSynchronizationRegistry) { 757 return (TransactionSynchronizationRegistry) ut; 758 } 759 if (tm instanceof TransactionSynchronizationRegistry) { 760 return (TransactionSynchronizationRegistry) tm; 761 } 762 // OK, so no JTA 1.1 TransactionSynchronizationRegistry is available... 763 return null; 764 } 765 766 767 /** 768 * This implementation returns a JtaTransactionObject instance for the 769 * JTA UserTransaction. 770 * <p>The UserTransaction object will either be looked up freshly for the 771 * current transaction, or the cached one looked up at startup will be used. 772 * The latter is the default: Most application servers use a shared singleton 773 * UserTransaction that can be cached. Turn off the "cacheUserTransaction" 774 * flag to enforce a fresh lookup for every transaction. 775 * @see #setCacheUserTransaction 776 */ 777 @Override 778 protected Object doGetTransaction() { 779 UserTransaction ut = getUserTransaction(); 780 if (ut == null) { 781 throw new CannotCreateTransactionException("No JTA UserTransaction available - " + 782 "programmatic PlatformTransactionManager.getTransaction usage not supported"); 783 } 784 if (!this.cacheUserTransaction) { 785 ut = lookupUserTransaction( 786 this.userTransactionName != null ? this.userTransactionName : DEFAULT_USER_TRANSACTION_NAME); 787 } 788 return doGetJtaTransaction(ut); 789 } 790 791 /** 792 * Get a JTA transaction object for the given current UserTransaction. 793 * <p>Subclasses can override this to provide a JtaTransactionObject 794 * subclass, for example holding some additional JTA handle needed. 795 * @param ut the UserTransaction handle to use for the current transaction 796 * @return the JtaTransactionObject holding the UserTransaction 797 */ 798 protected JtaTransactionObject doGetJtaTransaction(UserTransaction ut) { 799 return new JtaTransactionObject(ut); 800 } 801 802 @Override 803 protected boolean isExistingTransaction(Object transaction) { 804 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 805 try { 806 return (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION); 807 } 808 catch (SystemException ex) { 809 throw new TransactionSystemException("JTA failure on getStatus", ex); 810 } 811 } 812 813 /** 814 * This implementation returns false to cause a further invocation 815 * of doBegin despite an already existing transaction. 816 * <p>JTA implementations might support nested transactions via further 817 * {@code UserTransaction.begin()} invocations, but never support savepoints. 818 * @see #doBegin 819 * @see javax.transaction.UserTransaction#begin() 820 */ 821 @Override 822 protected boolean useSavepointForNestedTransaction() { 823 return false; 824 } 825 826 827 @Override 828 protected void doBegin(Object transaction, TransactionDefinition definition) { 829 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 830 try { 831 doJtaBegin(txObject, definition); 832 } 833 catch (NotSupportedException ex) { 834 throw new NestedTransactionNotSupportedException( 835 "JTA implementation does not support nested transactions", ex); 836 } 837 catch (UnsupportedOperationException ex) { 838 throw new NestedTransactionNotSupportedException( 839 "JTA implementation does not support nested transactions", ex); 840 } 841 catch (SystemException ex) { 842 throw new CannotCreateTransactionException("JTA failure on begin", ex); 843 } 844 } 845 846 /** 847 * Perform a JTA begin on the JTA UserTransaction or TransactionManager. 848 * <p>This implementation only supports standard JTA functionality: 849 * that is, no per-transaction isolation levels and no transaction names. 850 * Can be overridden in subclasses, for specific JTA implementations. 851 * <p>Calls {@code applyIsolationLevel} and {@code applyTimeout} 852 * before invoking the UserTransaction's {@code begin} method. 853 * @param txObject the JtaTransactionObject containing the UserTransaction 854 * @param definition TransactionDefinition instance, describing propagation 855 * behavior, isolation level, read-only flag, timeout, and transaction name 856 * @throws NotSupportedException if thrown by JTA methods 857 * @throws SystemException if thrown by JTA methods 858 * @see #getUserTransaction 859 * @see #getTransactionManager 860 * @see #applyIsolationLevel 861 * @see #applyTimeout 862 * @see JtaTransactionObject#getUserTransaction() 863 * @see javax.transaction.UserTransaction#setTransactionTimeout 864 * @see javax.transaction.UserTransaction#begin 865 */ 866 protected void doJtaBegin(JtaTransactionObject txObject, TransactionDefinition definition) 867 throws NotSupportedException, SystemException { 868 869 applyIsolationLevel(txObject, definition.getIsolationLevel()); 870 int timeout = determineTimeout(definition); 871 applyTimeout(txObject, timeout); 872 txObject.getUserTransaction().begin(); 873 } 874 875 /** 876 * Apply the given transaction isolation level. The default implementation 877 * will throw an exception for any level other than ISOLATION_DEFAULT. 878 * <p>To be overridden in subclasses for specific JTA implementations, 879 * as alternative to overriding the full {@link #doJtaBegin} method. 880 * @param txObject the JtaTransactionObject containing the UserTransaction 881 * @param isolationLevel isolation level taken from transaction definition 882 * @throws InvalidIsolationLevelException if the given isolation level 883 * cannot be applied 884 * @throws SystemException if thrown by the JTA implementation 885 * @see #doJtaBegin 886 * @see JtaTransactionObject#getUserTransaction() 887 * @see #getTransactionManager() 888 */ 889 protected void applyIsolationLevel(JtaTransactionObject txObject, int isolationLevel) 890 throws InvalidIsolationLevelException, SystemException { 891 892 if (!this.allowCustomIsolationLevels && isolationLevel != TransactionDefinition.ISOLATION_DEFAULT) { 893 throw new InvalidIsolationLevelException( 894 "JtaTransactionManager does not support custom isolation levels by default - " + 895 "switch 'allowCustomIsolationLevels' to 'true'"); 896 } 897 } 898 899 /** 900 * Apply the given transaction timeout. The default implementation will call 901 * {@code UserTransaction.setTransactionTimeout} for a non-default timeout value. 902 * @param txObject the JtaTransactionObject containing the UserTransaction 903 * @param timeout timeout value taken from transaction definition 904 * @throws SystemException if thrown by the JTA implementation 905 * @see #doJtaBegin 906 * @see JtaTransactionObject#getUserTransaction() 907 * @see javax.transaction.UserTransaction#setTransactionTimeout(int) 908 */ 909 protected void applyTimeout(JtaTransactionObject txObject, int timeout) throws SystemException { 910 if (timeout > TransactionDefinition.TIMEOUT_DEFAULT) { 911 txObject.getUserTransaction().setTransactionTimeout(timeout); 912 if (timeout > 0) { 913 txObject.resetTransactionTimeout = true; 914 } 915 } 916 } 917 918 919 @Override 920 protected Object doSuspend(Object transaction) { 921 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 922 try { 923 return doJtaSuspend(txObject); 924 } 925 catch (SystemException ex) { 926 throw new TransactionSystemException("JTA failure on suspend", ex); 927 } 928 } 929 930 /** 931 * Perform a JTA suspend on the JTA TransactionManager. 932 * <p>Can be overridden in subclasses, for specific JTA implementations. 933 * @param txObject the JtaTransactionObject containing the UserTransaction 934 * @return the suspended JTA Transaction object 935 * @throws SystemException if thrown by JTA methods 936 * @see #getTransactionManager() 937 * @see javax.transaction.TransactionManager#suspend() 938 */ 939 protected Object doJtaSuspend(JtaTransactionObject txObject) throws SystemException { 940 if (getTransactionManager() == null) { 941 throw new TransactionSuspensionNotSupportedException( 942 "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: " + 943 "specify the 'transactionManager' or 'transactionManagerName' property"); 944 } 945 return getTransactionManager().suspend(); 946 } 947 948 @Override 949 protected void doResume(Object transaction, Object suspendedResources) { 950 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 951 try { 952 doJtaResume(txObject, suspendedResources); 953 } 954 catch (InvalidTransactionException ex) { 955 throw new IllegalTransactionStateException("Tried to resume invalid JTA transaction", ex); 956 } 957 catch (IllegalStateException ex) { 958 throw new TransactionSystemException("Unexpected internal transaction state", ex); 959 } 960 catch (SystemException ex) { 961 throw new TransactionSystemException("JTA failure on resume", ex); 962 } 963 } 964 965 /** 966 * Perform a JTA resume on the JTA TransactionManager. 967 * <p>Can be overridden in subclasses, for specific JTA implementations. 968 * @param txObject the JtaTransactionObject containing the UserTransaction 969 * @param suspendedTransaction the suspended JTA Transaction object 970 * @throws InvalidTransactionException if thrown by JTA methods 971 * @throws SystemException if thrown by JTA methods 972 * @see #getTransactionManager() 973 * @see javax.transaction.TransactionManager#resume(javax.transaction.Transaction) 974 */ 975 protected void doJtaResume(JtaTransactionObject txObject, Object suspendedTransaction) 976 throws InvalidTransactionException, SystemException { 977 978 if (getTransactionManager() == null) { 979 throw new TransactionSuspensionNotSupportedException( 980 "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: " + 981 "specify the 'transactionManager' or 'transactionManagerName' property"); 982 } 983 getTransactionManager().resume((Transaction) suspendedTransaction); 984 } 985 986 987 /** 988 * This implementation returns "true": a JTA commit will properly handle 989 * transactions that have been marked rollback-only at a global level. 990 */ 991 @Override 992 protected boolean shouldCommitOnGlobalRollbackOnly() { 993 return true; 994 } 995 996 @Override 997 protected void doCommit(DefaultTransactionStatus status) { 998 JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction(); 999 try { 1000 int jtaStatus = txObject.getUserTransaction().getStatus(); 1001 if (jtaStatus == Status.STATUS_NO_TRANSACTION) { 1002 // Should never happen... would have thrown an exception before 1003 // and as a consequence led to a rollback, not to a commit call. 1004 // In any case, the transaction is already fully cleaned up. 1005 throw new UnexpectedRollbackException("JTA transaction already completed - probably rolled back"); 1006 } 1007 if (jtaStatus == Status.STATUS_ROLLEDBACK) { 1008 // Only really happens on JBoss 4.2 in case of an early timeout... 1009 // Explicit rollback call necessary to clean up the transaction. 1010 // IllegalStateException expected on JBoss; call still necessary. 1011 try { 1012 txObject.getUserTransaction().rollback(); 1013 } 1014 catch (IllegalStateException ex) { 1015 if (logger.isDebugEnabled()) { 1016 logger.debug("Rollback failure with transaction already marked as rolled back: " + ex); 1017 } 1018 } 1019 throw new UnexpectedRollbackException("JTA transaction already rolled back (probably due to a timeout)"); 1020 } 1021 txObject.getUserTransaction().commit(); 1022 } 1023 catch (RollbackException ex) { 1024 throw new UnexpectedRollbackException( 1025 "JTA transaction unexpectedly rolled back (maybe due to a timeout)", ex); 1026 } 1027 catch (HeuristicMixedException ex) { 1028 throw new HeuristicCompletionException(HeuristicCompletionException.STATE_MIXED, ex); 1029 } 1030 catch (HeuristicRollbackException ex) { 1031 throw new HeuristicCompletionException(HeuristicCompletionException.STATE_ROLLED_BACK, ex); 1032 } 1033 catch (IllegalStateException ex) { 1034 throw new TransactionSystemException("Unexpected internal transaction state", ex); 1035 } 1036 catch (SystemException ex) { 1037 throw new TransactionSystemException("JTA failure on commit", ex); 1038 } 1039 } 1040 1041 @Override 1042 protected void doRollback(DefaultTransactionStatus status) { 1043 JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction(); 1044 try { 1045 int jtaStatus = txObject.getUserTransaction().getStatus(); 1046 if (jtaStatus != Status.STATUS_NO_TRANSACTION) { 1047 try { 1048 txObject.getUserTransaction().rollback(); 1049 } 1050 catch (IllegalStateException ex) { 1051 if (jtaStatus == Status.STATUS_ROLLEDBACK) { 1052 // Only really happens on JBoss 4.2 in case of an early timeout... 1053 if (logger.isDebugEnabled()) { 1054 logger.debug("Rollback failure with transaction already marked as rolled back: " + ex); 1055 } 1056 } 1057 else { 1058 throw new TransactionSystemException("Unexpected internal transaction state", ex); 1059 } 1060 } 1061 } 1062 } 1063 catch (SystemException ex) { 1064 throw new TransactionSystemException("JTA failure on rollback", ex); 1065 } 1066 } 1067 1068 @Override 1069 protected void doSetRollbackOnly(DefaultTransactionStatus status) { 1070 JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction(); 1071 if (status.isDebug()) { 1072 logger.debug("Setting JTA transaction rollback-only"); 1073 } 1074 try { 1075 int jtaStatus = txObject.getUserTransaction().getStatus(); 1076 if (jtaStatus != Status.STATUS_NO_TRANSACTION && jtaStatus != Status.STATUS_ROLLEDBACK) { 1077 txObject.getUserTransaction().setRollbackOnly(); 1078 } 1079 } 1080 catch (IllegalStateException ex) { 1081 throw new TransactionSystemException("Unexpected internal transaction state", ex); 1082 } 1083 catch (SystemException ex) { 1084 throw new TransactionSystemException("JTA failure on setRollbackOnly", ex); 1085 } 1086 } 1087 1088 1089 @Override 1090 protected void registerAfterCompletionWithExistingTransaction( 1091 Object transaction, List<TransactionSynchronization> synchronizations) { 1092 1093 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 1094 logger.debug("Registering after-completion synchronization with existing JTA transaction"); 1095 try { 1096 doRegisterAfterCompletionWithJtaTransaction(txObject, synchronizations); 1097 } 1098 catch (SystemException ex) { 1099 throw new TransactionSystemException("JTA failure on registerSynchronization", ex); 1100 } 1101 catch (Exception ex) { 1102 // Note: JBoss throws plain RuntimeException with RollbackException as cause. 1103 if (ex instanceof RollbackException || ex.getCause() instanceof RollbackException) { 1104 logger.debug("Participating in existing JTA transaction that has been marked for rollback: " + 1105 "cannot register Spring after-completion callbacks with outer JTA transaction - " + 1106 "immediately performing Spring after-completion callbacks with outcome status 'rollback'. " + 1107 "Original exception: " + ex); 1108 invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_ROLLED_BACK); 1109 } 1110 else { 1111 logger.debug("Participating in existing JTA transaction, but unexpected internal transaction " + 1112 "state encountered: cannot register Spring after-completion callbacks with outer JTA " + 1113 "transaction - processing Spring after-completion callbacks with outcome status 'unknown'" + 1114 "Original exception: " + ex); 1115 invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN); 1116 } 1117 } 1118 } 1119 1120 /** 1121 * Register a JTA synchronization on the JTA TransactionManager, for calling 1122 * {@code afterCompletion} on the given Spring TransactionSynchronizations. 1123 * <p>The default implementation registers the synchronizations on the 1124 * JTA 1.1 TransactionSynchronizationRegistry, if available, or on the 1125 * JTA TransactionManager's current Transaction - again, if available. 1126 * If none of the two is available, a warning will be logged. 1127 * <p>Can be overridden in subclasses, for specific JTA implementations. 1128 * @param txObject the current transaction object 1129 * @param synchronizations List of TransactionSynchronization objects 1130 * @throws RollbackException if thrown by JTA methods 1131 * @throws SystemException if thrown by JTA methods 1132 * @see #getTransactionManager() 1133 * @see javax.transaction.Transaction#registerSynchronization 1134 * @see javax.transaction.TransactionSynchronizationRegistry#registerInterposedSynchronization 1135 */ 1136 protected void doRegisterAfterCompletionWithJtaTransaction( 1137 JtaTransactionObject txObject, List<TransactionSynchronization> synchronizations) 1138 throws RollbackException, SystemException { 1139 1140 int jtaStatus = txObject.getUserTransaction().getStatus(); 1141 if (jtaStatus == Status.STATUS_NO_TRANSACTION) { 1142 throw new RollbackException("JTA transaction already completed - probably rolled back"); 1143 } 1144 if (jtaStatus == Status.STATUS_ROLLEDBACK) { 1145 throw new RollbackException("JTA transaction already rolled back (probably due to a timeout)"); 1146 } 1147 1148 if (this.transactionSynchronizationRegistry != null) { 1149 // JTA 1.1 TransactionSynchronizationRegistry available - use it. 1150 this.transactionSynchronizationRegistry.registerInterposedSynchronization( 1151 new JtaAfterCompletionSynchronization(synchronizations)); 1152 } 1153 1154 else if (getTransactionManager() != null) { 1155 // At least the JTA TransactionManager available - use that one. 1156 Transaction transaction = getTransactionManager().getTransaction(); 1157 if (transaction == null) { 1158 throw new IllegalStateException("No JTA Transaction available"); 1159 } 1160 transaction.registerSynchronization(new JtaAfterCompletionSynchronization(synchronizations)); 1161 } 1162 1163 else { 1164 // No JTA TransactionManager available - log a warning. 1165 logger.warn("Participating in existing JTA transaction, but no JTA TransactionManager available: " + 1166 "cannot register Spring after-completion callbacks with outer JTA transaction - " + 1167 "processing Spring after-completion callbacks with outcome status 'unknown'"); 1168 invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN); 1169 } 1170 } 1171 1172 @Override 1173 protected void doCleanupAfterCompletion(Object transaction) { 1174 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 1175 if (txObject.resetTransactionTimeout) { 1176 try { 1177 txObject.getUserTransaction().setTransactionTimeout(0); 1178 } 1179 catch (SystemException ex) { 1180 logger.debug("Failed to reset transaction timeout after JTA completion", ex); 1181 } 1182 } 1183 } 1184 1185 1186 //--------------------------------------------------------------------- 1187 // Implementation of TransactionFactory interface 1188 //--------------------------------------------------------------------- 1189 1190 @Override 1191 public Transaction createTransaction(String name, int timeout) throws NotSupportedException, SystemException { 1192 TransactionManager tm = getTransactionManager(); 1193 Assert.state(tm != null, "No JTA TransactionManager available"); 1194 if (timeout >= 0) { 1195 tm.setTransactionTimeout(timeout); 1196 } 1197 tm.begin(); 1198 return new ManagedTransactionAdapter(tm); 1199 } 1200 1201 @Override 1202 public boolean supportsResourceAdapterManagedTransactions() { 1203 return false; 1204 } 1205 1206 1207 //--------------------------------------------------------------------- 1208 // Serialization support 1209 //--------------------------------------------------------------------- 1210 1211 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 1212 // Rely on default serialization; just initialize state after deserialization. 1213 ois.defaultReadObject(); 1214 1215 // Create template for client-side JNDI lookup. 1216 this.jndiTemplate = new JndiTemplate(); 1217 1218 // Perform a fresh lookup for JTA handles. 1219 initUserTransactionAndTransactionManager(); 1220 initTransactionSynchronizationRegistry(); 1221 } 1222 1223}