1 16 17 package org.springframework.transaction.jta; 18 19 import java.io.IOException ; 20 import java.io.ObjectInputStream ; 21 import java.io.Serializable ; 22 import java.util.List ; 23 import java.util.Properties ; 24 25 import javax.naming.NamingException ; 26 import javax.transaction.HeuristicMixedException ; 27 import javax.transaction.HeuristicRollbackException ; 28 import javax.transaction.InvalidTransactionException ; 29 import javax.transaction.NotSupportedException ; 30 import javax.transaction.RollbackException ; 31 import javax.transaction.Status ; 32 import javax.transaction.SystemException ; 33 import javax.transaction.Transaction ; 34 import javax.transaction.TransactionManager ; 35 import javax.transaction.UserTransaction ; 36 37 import org.springframework.beans.factory.InitializingBean; 38 import org.springframework.jndi.JndiTemplate; 39 import org.springframework.transaction.CannotCreateTransactionException; 40 import org.springframework.transaction.HeuristicCompletionException; 41 import org.springframework.transaction.IllegalTransactionStateException; 42 import org.springframework.transaction.InvalidIsolationLevelException; 43 import org.springframework.transaction.NestedTransactionNotSupportedException; 44 import org.springframework.transaction.NoTransactionException; 45 import org.springframework.transaction.TransactionDefinition; 46 import org.springframework.transaction.TransactionSuspensionNotSupportedException; 47 import org.springframework.transaction.TransactionSystemException; 48 import org.springframework.transaction.UnexpectedRollbackException; 49 import org.springframework.transaction.support.AbstractPlatformTransactionManager; 50 import org.springframework.transaction.support.DefaultTransactionStatus; 51 import org.springframework.transaction.support.TransactionSynchronization; 52 import org.springframework.util.Assert; 53 54 153 public class JtaTransactionManager extends AbstractPlatformTransactionManager 154 implements InitializingBean, Serializable { 155 156 162 public static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction"; 163 164 171 public static final String [] FALLBACK_TRANSACTION_MANAGER_NAMES = 172 new String [] {"java:comp/TransactionManager", "java:pm/TransactionManager", "java:/TransactionManager"}; 173 174 175 private transient JndiTemplate jndiTemplate = new JndiTemplate(); 176 177 private transient UserTransaction userTransaction; 178 179 private String userTransactionName; 180 181 private boolean autodetectUserTransaction = true; 182 183 private boolean cacheUserTransaction = true; 184 185 private transient TransactionManager transactionManager; 186 187 private String transactionManagerName; 188 189 private boolean autodetectTransactionManager = true; 190 191 private boolean allowCustomIsolationLevels = false; 192 193 194 203 public JtaTransactionManager() { 204 setNestedTransactionAllowed(true); 205 } 206 207 211 public JtaTransactionManager(UserTransaction userTransaction) { 212 this(); 213 Assert.notNull(userTransaction, "UserTransaction must not be null"); 214 this.userTransaction = userTransaction; 215 } 216 217 222 public JtaTransactionManager(UserTransaction userTransaction, TransactionManager transactionManager) { 223 this(); 224 Assert.notNull(userTransaction, "UserTransaction must not be null"); 225 Assert.notNull(transactionManager, "TransactionManager must not be null"); 226 this.userTransaction = userTransaction; 227 this.transactionManager = transactionManager; 228 } 229 230 234 public JtaTransactionManager(TransactionManager transactionManager) { 235 this(); 236 Assert.notNull(transactionManager, "TransactionManager must not be null"); 237 this.transactionManager = transactionManager; 238 this.userTransaction = buildUserTransaction(transactionManager); 239 } 240 241 242 246 public void setJndiTemplate(JndiTemplate jndiTemplate) { 247 if (jndiTemplate == null) { 248 throw new IllegalArgumentException ("jndiTemplate must not be null"); 249 } 250 this.jndiTemplate = jndiTemplate; 251 } 252 253 256 public JndiTemplate getJndiTemplate() { 257 return this.jndiTemplate; 258 } 259 260 265 public void setJndiEnvironment(Properties jndiEnvironment) { 266 this.jndiTemplate = new JndiTemplate(jndiEnvironment); 267 } 268 269 272 public Properties getJndiEnvironment() { 273 return this.jndiTemplate.getEnvironment(); 274 } 275 276 277 284 public void setUserTransaction(UserTransaction userTransaction) { 285 this.userTransaction = userTransaction; 286 } 287 288 291 public UserTransaction getUserTransaction() { 292 return this.userTransaction; 293 } 294 295 303 public void setUserTransactionName(String userTransactionName) { 304 this.userTransactionName = userTransactionName; 305 } 306 307 317 public void setAutodetectUserTransaction(boolean autodetectUserTransaction) { 318 this.autodetectUserTransaction = autodetectUserTransaction; 319 } 320 321 333 public void setCacheUserTransaction(boolean cacheUserTransaction) { 334 this.cacheUserTransaction = cacheUserTransaction; 335 } 336 337 338 348 public void setTransactionManager(TransactionManager transactionManager) { 349 this.transactionManager = transactionManager; 350 } 351 352 355 public TransactionManager getTransactionManager() { 356 return this.transactionManager; 357 } 358 359 369 public void setTransactionManagerName(String transactionManagerName) { 370 this.transactionManagerName = transactionManagerName; 371 } 372 373 385 public void setAutodetectTransactionManager(boolean autodetectTransactionManager) { 386 this.autodetectTransactionManager = autodetectTransactionManager; 387 } 388 389 397 public void setAllowCustomIsolationLevels(boolean allowCustomIsolationLevels) { 398 this.allowCustomIsolationLevels = allowCustomIsolationLevels; 399 } 400 401 402 406 public void afterPropertiesSet() throws TransactionSystemException { 407 initUserTransactionAndTransactionManager(); 408 } 409 410 414 protected void initUserTransactionAndTransactionManager() throws TransactionSystemException { 415 if (this.userTransaction == null) { 417 if (this.userTransactionName != null) { 418 this.userTransaction = lookupUserTransaction(this.userTransactionName); 419 } 420 else { 421 this.userTransaction = retrieveUserTransaction(); 422 } 423 } 424 425 if (this.transactionManager == null) { 427 if (this.transactionManagerName != null) { 428 this.transactionManager = lookupTransactionManager(this.transactionManagerName); 429 } 430 else { 431 this.transactionManager = retrieveTransactionManager(); 432 } 433 } 434 435 if (this.userTransaction == null && this.autodetectUserTransaction) { 437 this.userTransaction = findUserTransaction(); 438 } 439 440 if (this.transactionManager == null && this.autodetectTransactionManager) { 443 this.transactionManager = findTransactionManager(this.userTransaction); 444 } 445 446 if (this.userTransaction == null && this.transactionManager != null) { 448 this.userTransaction = buildUserTransaction(this.transactionManager); 449 } 450 451 if (this.userTransaction != null) { 453 if (logger.isInfoEnabled()) { 454 logger.info("Using JTA UserTransaction: " + this.userTransaction); 455 } 456 } 457 else { 458 throw new IllegalStateException ( 459 "Either 'userTransaction' or 'userTransactionName' or 'transactionManager' " + 460 "or 'transactionManagerName' must be specified"); 461 } 462 463 if (this.transactionManager != null) { 465 if (logger.isInfoEnabled()) { 466 logger.info("Using JTA TransactionManager: " + this.transactionManager); 467 } 468 } 469 else { 470 logger.warn("No JTA TransactionManager found: " + 471 "transaction suspension and synchronization with existing JTA transactions not available"); 472 } 473 } 474 475 476 486 protected UserTransaction lookupUserTransaction(String userTransactionName) 487 throws TransactionSystemException { 488 try { 489 if (logger.isDebugEnabled()) { 490 logger.debug("Retrieving JTA UserTransaction from JNDI location [" + userTransactionName + "]"); 491 } 492 return (UserTransaction ) getJndiTemplate().lookup(userTransactionName, UserTransaction .class); 493 } 494 catch (NamingException ex) { 495 throw new TransactionSystemException( 496 "JTA UserTransaction is not available at JNDI location [" + userTransactionName + "]", ex); 497 } 498 } 499 500 510 protected TransactionManager lookupTransactionManager(String transactionManagerName) 511 throws TransactionSystemException { 512 try { 513 if (logger.isDebugEnabled()) { 514 logger.debug("Retrieving JTA TransactionManager from JNDI location [" + transactionManagerName + "]"); 515 } 516 return (TransactionManager ) getJndiTemplate().lookup(transactionManagerName, TransactionManager .class); 517 } 518 catch (NamingException ex) { 519 throw new TransactionSystemException( 520 "JTA TransactionManager is not available at JNDI location [" + transactionManagerName + "]", ex); 521 } 522 } 523 524 533 protected UserTransaction retrieveUserTransaction() throws TransactionSystemException { 534 return null; 535 } 536 537 546 protected TransactionManager retrieveTransactionManager() throws TransactionSystemException { 547 return null; 548 } 549 550 556 protected UserTransaction findUserTransaction() { 557 String jndiName = DEFAULT_USER_TRANSACTION_NAME; 558 try { 559 UserTransaction ut = (UserTransaction ) getJndiTemplate().lookup(jndiName, UserTransaction .class); 560 if (logger.isDebugEnabled()) { 561 logger.debug("JTA UserTransaction found at default JNDI location [" + jndiName + "]"); 562 } 563 return ut; 564 } 565 catch (NamingException ex) { 566 if (logger.isDebugEnabled()) { 567 logger.debug("No JTA UserTransaction found at default JNDI location [" + jndiName + "]", ex); 568 } 569 return null; 570 } 571 } 572 573 581 protected TransactionManager findTransactionManager(UserTransaction ut) { 582 if (ut instanceof TransactionManager ) { 583 if (logger.isDebugEnabled()) { 584 logger.debug("JTA UserTransaction object [" + ut + "] implements TransactionManager"); 585 } 586 return (TransactionManager ) ut; 587 } 588 589 for (int i = 0; i < FALLBACK_TRANSACTION_MANAGER_NAMES.length; i++) { 591 String jndiName = FALLBACK_TRANSACTION_MANAGER_NAMES[i]; 592 try { 593 TransactionManager tm = (TransactionManager ) getJndiTemplate().lookup(jndiName, TransactionManager .class); 594 if (logger.isDebugEnabled()) { 595 logger.debug("JTA TransactionManager found at fallback JNDI location [" + jndiName + "]"); 596 } 597 return tm; 598 } 599 catch (NamingException ex) { 600 if (logger.isDebugEnabled()) { 601 logger.debug("No JTA TransactionManager found at fallback JNDI location [" + jndiName + "]", ex); 602 } 603 } 604 } 605 606 return null; 608 } 609 610 615 protected UserTransaction buildUserTransaction(TransactionManager transactionManager) { 616 if (transactionManager instanceof UserTransaction ) { 617 return (UserTransaction ) transactionManager; 618 } 619 else { 620 return new UserTransactionAdapter(transactionManager); 621 } 622 } 623 624 625 635 protected Object doGetTransaction() { 636 UserTransaction ut = getUserTransaction(); 637 if (!this.cacheUserTransaction) { 638 ut = lookupUserTransaction( 639 this.userTransactionName != null ? this.userTransactionName : DEFAULT_USER_TRANSACTION_NAME); 640 } 641 return doGetJtaTransaction(ut); 642 } 643 644 651 protected JtaTransactionObject doGetJtaTransaction(UserTransaction ut) { 652 return new JtaTransactionObject(ut); 653 } 654 655 protected boolean isExistingTransaction(Object transaction) { 656 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 657 try { 658 return (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION); 659 } 660 catch (SystemException ex) { 661 throw new TransactionSystemException("JTA failure on getStatus", ex); 662 } 663 } 664 665 673 protected boolean useSavepointForNestedTransaction() { 674 return false; 675 } 676 677 678 protected void doBegin(Object transaction, TransactionDefinition definition) { 679 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 680 try { 681 doJtaBegin(txObject, definition); 682 } 683 catch (NotSupportedException ex) { 684 throw new NestedTransactionNotSupportedException( 686 "JTA implementation does not support nested transactions", ex); 687 } 688 catch (UnsupportedOperationException ex) { 689 throw new NestedTransactionNotSupportedException( 691 "JTA implementation does not support nested transactions", ex); 692 } 693 catch (SystemException ex) { 694 throw new CannotCreateTransactionException("JTA failure on begin", ex); 695 } 696 } 697 698 718 protected void doJtaBegin(JtaTransactionObject txObject, TransactionDefinition definition) 719 throws NotSupportedException , SystemException { 720 721 applyIsolationLevel(txObject, definition.getIsolationLevel()); 722 int timeout = determineTimeout(definition); 723 applyTimeout(txObject, timeout); 724 txObject.getUserTransaction().begin(); 725 } 726 727 741 protected void applyIsolationLevel(JtaTransactionObject txObject, int isolationLevel) 742 throws InvalidIsolationLevelException, SystemException { 743 744 if (!this.allowCustomIsolationLevels && isolationLevel != TransactionDefinition.ISOLATION_DEFAULT) { 745 throw new InvalidIsolationLevelException( 746 "JtaTransactionManager does not support custom isolation levels by default - " + 747 "switch 'allowCustomIsolationLevels' to 'true'"); 748 } 749 } 750 751 761 protected void applyTimeout(JtaTransactionObject txObject, int timeout) throws SystemException { 762 if (timeout > TransactionDefinition.TIMEOUT_DEFAULT) { 763 txObject.getUserTransaction().setTransactionTimeout(timeout); 764 } 765 } 766 767 768 protected Object doSuspend(Object transaction) { 769 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 770 try { 771 return doJtaSuspend(txObject); 772 } 773 catch (SystemException ex) { 774 throw new TransactionSystemException("JTA failure on suspend", ex); 775 } 776 } 777 778 787 protected Object doJtaSuspend(JtaTransactionObject txObject) throws SystemException { 788 if (getTransactionManager() == null) { 789 throw new TransactionSuspensionNotSupportedException( 790 "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: " + 791 "specify the 'transactionManager' or 'transactionManagerName' property"); 792 } 793 return getTransactionManager().suspend(); 794 } 795 796 protected void doResume(Object transaction, Object suspendedResources) { 797 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 798 try { 799 doJtaResume(txObject, suspendedResources); 800 } 801 catch (InvalidTransactionException ex) { 802 throw new IllegalTransactionStateException("Tried to resume invalid JTA transaction", ex); 803 } 804 catch (SystemException ex) { 805 throw new TransactionSystemException("JTA failure on resume", ex); 806 } 807 } 808 809 819 protected void doJtaResume(JtaTransactionObject txObject, Object suspendedTransaction) 820 throws InvalidTransactionException , SystemException { 821 822 if (getTransactionManager() == null) { 823 throw new TransactionSuspensionNotSupportedException( 824 "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: " + 825 "specify the 'transactionManager' or 'transactionManagerName' property"); 826 } 827 getTransactionManager().resume((Transaction ) suspendedTransaction); 828 } 829 830 831 835 protected boolean shouldCommitOnGlobalRollbackOnly() { 836 return true; 837 } 838 839 protected void doCommit(DefaultTransactionStatus status) { 840 JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction(); 841 try { 842 txObject.getUserTransaction().commit(); 843 } 844 catch (RollbackException ex) { 845 throw new UnexpectedRollbackException( 846 "JTA transaction unexpectedly rolled back (maybe due to a timeout)", ex); 847 } 848 catch (HeuristicMixedException ex) { 849 throw new HeuristicCompletionException(HeuristicCompletionException.STATE_MIXED, ex); 850 } 851 catch (HeuristicRollbackException ex) { 852 throw new HeuristicCompletionException(HeuristicCompletionException.STATE_ROLLED_BACK, ex); 853 } 854 catch (SystemException ex) { 855 throw new TransactionSystemException("JTA failure on commit", ex); 856 } 857 } 858 859 protected void doRollback(DefaultTransactionStatus status) { 860 JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction(); 861 try { 862 if (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION) { 863 txObject.getUserTransaction().rollback(); 864 } 865 } 866 catch (SystemException ex) { 867 throw new TransactionSystemException("JTA failure on rollback", ex); 868 } 869 } 870 871 protected void doSetRollbackOnly(DefaultTransactionStatus status) { 872 JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction(); 873 if (status.isDebug()) { 874 logger.debug("Setting JTA transaction rollback-only"); 875 } 876 try { 877 if (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION) { 878 txObject.getUserTransaction().setRollbackOnly(); 879 } 880 } 881 catch (IllegalStateException ex) { 882 throw new NoTransactionException("No active JTA transaction"); 883 } 884 catch (SystemException ex) { 885 throw new TransactionSystemException("JTA failure on setRollbackOnly", ex); 886 } 887 } 888 889 890 protected void registerAfterCompletionWithExistingTransaction(Object transaction, List synchronizations) { 891 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 892 logger.debug("Registering after-completion synchronization with existing JTA transaction"); 893 try { 894 doRegisterAfterCompletionWithJtaTransaction(txObject, synchronizations); 895 } 896 catch (RollbackException ex) { 897 logger.debug("Participating in existing JTA transaction that has been marked rollback-only: " + 898 "cannot register Spring after-completion callbacks with outer JTA transaction - " + 899 "immediately performing Spring after-completion callbacks with outcome status 'rollback'"); 900 invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_ROLLED_BACK); 901 } 902 catch (IllegalStateException ex) { 903 throw new NoTransactionException("No active JTA transaction"); 904 } 905 catch (SystemException ex) { 906 throw new TransactionSystemException("JTA failure on registerSynchronization", ex); 907 } 908 } 909 910 921 protected void doRegisterAfterCompletionWithJtaTransaction( 922 JtaTransactionObject txObject, List synchronizations) 923 throws RollbackException , SystemException { 924 925 if (getTransactionManager() != null) { 926 Transaction transaction = getTransactionManager().getTransaction(); 927 if (transaction != null) { 928 transaction.registerSynchronization(new JtaAfterCompletionSynchronization(synchronizations)); 929 } 930 else { 931 logger.debug("Participating in existing JTA transaction, but no current JTA Transaction available: " + 933 "cannot register Spring after-completion callbacks with outer JTA transaction - " + 934 "processing Spring after-completion callbacks with outcome status 'unknown'"); 935 invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN); 936 } 937 } 938 else { 939 logger.warn("Participating in existing JTA transaction, but no JTA TransactionManager available: " + 941 "cannot register Spring after-completion callbacks with outer JTA transaction - " + 942 "processing Spring after-completion callbacks with outcome status 'unknown'"); 943 invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN); 944 } 945 } 946 947 948 952 private void readObject(ObjectInputStream ois) throws IOException , ClassNotFoundException { 953 ois.defaultReadObject(); 955 956 this.jndiTemplate = new JndiTemplate(); 958 959 initUserTransactionAndTransactionManager(); 961 } 962 963 } 964 | Popular Tags |