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