1 22 package org.jboss.resource.connectionmanager; 23 24 import java.util.ArrayList ; 25 import java.util.Collection ; 26 import java.util.HashSet ; 27 import java.util.Iterator ; 28 import java.util.Set ; 29 30 import javax.management.ObjectName ; 31 import javax.naming.InitialContext ; 32 import javax.resource.ResourceException ; 33 import javax.resource.spi.ConnectionEvent ; 34 import javax.resource.spi.ConnectionRequestInfo ; 35 import javax.resource.spi.LocalTransaction ; 36 import javax.resource.spi.ManagedConnection ; 37 import javax.security.auth.Subject ; 38 import javax.transaction.RollbackException ; 39 import javax.transaction.Status ; 40 import javax.transaction.Synchronization ; 41 import javax.transaction.SystemException ; 42 import javax.transaction.Transaction ; 43 import javax.transaction.TransactionManager ; 44 import javax.transaction.xa.XAException ; 45 import javax.transaction.xa.XAResource ; 46 import javax.transaction.xa.Xid ; 47 48 import org.jboss.logging.Logger; 49 import org.jboss.resource.JBossResourceException; 50 import org.jboss.resource.connectionmanager.xa.XAResourceWrapper; 51 import org.jboss.tm.LastResource; 52 import org.jboss.tm.TransactionTimeoutConfiguration; 53 import org.jboss.tm.TxUtils; 54 import org.jboss.util.NestedRuntimeException; 55 56 import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean; 57 58 130 public class TxConnectionManager extends BaseConnectionManager2 implements TxConnectionManagerMBean 131 { 132 private static final Throwable FAILED_TO_ENLIST = new Throwable ("Unabled to enlist resource, see the previous warnings."); 133 134 private ObjectName transactionManagerService; 135 136 private String tmName; 138 139 private TransactionManager tm; 140 141 private boolean trackConnectionByTx = false; 142 143 private boolean localTransactions; 144 145 private int xaResourceTimeout = 0; 146 147 private boolean padXid; 148 149 private boolean wrapXAResource; 150 151 private Boolean isSameRMOverrideValue; 152 153 protected static void rethrowAsSystemException(String context, Transaction tx, Throwable t) 154 throws SystemException 155 { 156 if (t instanceof SystemException ) 157 throw (SystemException ) t; 158 if (t instanceof RuntimeException ) 159 throw (RuntimeException ) t; 160 if (t instanceof Error ) 161 throw (Error ) t; 162 if (t instanceof RollbackException ) 163 throw new IllegalStateException (context + " tx=" + tx + " marked for rollback."); 164 throw new NestedRuntimeException(context + " tx=" + tx + " got unexpected error ", t); 165 } 166 167 170 public TxConnectionManager() 171 { 172 } 173 174 182 public TxConnectionManager (final CachedConnectionManager ccm, 183 final ManagedConnectionPool poolingStrategy, 184 final TransactionManager tm) 185 { 186 super(ccm, poolingStrategy); 187 this.tm = tm; 188 } 189 190 public ObjectName getTransactionManagerService() 191 { 192 return transactionManagerService; 193 } 194 195 public void setTransactionManagerService(ObjectName transactionManagerService) 196 { 197 this.transactionManagerService = transactionManagerService; 198 } 199 200 203 public void setTransactionManager(final String tmName) 204 { 205 this.tmName = tmName; 206 } 207 208 211 public String getTransactionManager() 212 { 213 return this.tmName; 214 } 215 216 public TransactionManager getTransactionManagerInstance() 217 { 218 return tm; 219 } 220 221 public void setTransactionManagerInstance(TransactionManager tm) 222 { 223 this.tm = tm; 224 } 225 226 public boolean isTrackConnectionByTx() 227 { 228 return trackConnectionByTx; 229 } 230 231 public void setTrackConnectionByTx(boolean trackConnectionByTx) 232 { 233 this.trackConnectionByTx = trackConnectionByTx; 234 } 235 236 public boolean isLocalTransactions() 237 { 238 return localTransactions; 239 } 240 241 public void setLocalTransactions(boolean localTransactions) 242 { 243 this.localTransactions = localTransactions; 244 } 245 246 public int getXAResourceTransactionTimeout() 247 { 248 return xaResourceTimeout; 249 } 250 251 public void setXAResourceTransactionTimeout(int timeout) 252 { 253 this.xaResourceTimeout = timeout; 254 } 255 256 261 public Boolean getIsSameRMOverrideValue() 262 { 263 return isSameRMOverrideValue; 264 } 265 266 public boolean getWrapXAResource() 267 { 268 return wrapXAResource; 269 } 270 271 public void setWrapXAResource(boolean useXAWrapper) 272 { 273 this.wrapXAResource = useXAWrapper; 274 275 } 276 277 public boolean getPadXid() 278 { 279 return this.padXid; 280 281 } 282 283 public void setPadXid(boolean padXid) 284 { 285 this.padXid = padXid; 286 } 287 292 public void setIsSameRMOverrideValue(Boolean isSameRMOverrideValue) 293 { 294 this.isSameRMOverrideValue = isSameRMOverrideValue; 295 } 296 297 public long getTimeLeftBeforeTransactionTimeout(boolean errorRollback) throws RollbackException 298 { 299 if (tm == null) 300 throw new IllegalStateException ("No transaction manager: " + ccmName); 301 if (tm instanceof TransactionTimeoutConfiguration) 302 return ((TransactionTimeoutConfiguration) tm).getTimeLeftBeforeTransactionTimeout(errorRollback); 303 return -1; 304 } 305 306 protected void startService() throws Exception 307 { 308 if (transactionManagerService != null) 309 tm = (TransactionManager )getServer().getAttribute(transactionManagerService, "TransactionManager"); 310 else 311 { 312 log.warn("----------------------------------------------------------"); 313 log.warn("----------------------------------------------------------"); 314 log.warn("Please change your datasource setup to use <depends optional-attribute-name\"TransactionManagerService\">jboss:service=TransactionManager</depends>"); 315 log.warn("instead of <attribute name=\"TransactionManager\">java:/TransactionManager</attribute>"); 316 log.warn("Better still, use a *-ds.xml file"); 317 log.warn("----------------------------------------------------------"); 318 log.warn("----------------------------------------------------------"); 319 tm = (TransactionManager )new InitialContext ().lookup(tmName); 320 } 321 322 323 super.startService(); 324 } 325 326 protected void stopService() throws Exception 327 { 328 this.tm = null; 329 super.stopService(); 330 } 331 332 public ConnectionListener getManagedConnection(Subject subject, ConnectionRequestInfo cri) 333 throws ResourceException 334 { 335 Transaction trackByTransaction = null; 336 try 337 { 338 Transaction tx = tm.getTransaction(); 339 if (tx != null && TxUtils.isActive(tx) == false) 340 throw new ResourceException ("Transaction is not active: tx=" + tx); 341 if (trackConnectionByTx) 342 trackByTransaction = tx; 343 } 344 catch (Throwable t) 345 { 346 JBossResourceException.rethrowAsResourceException("Error checking for a transaction.", t); 347 } 348 349 if (trace) 350 log.trace("getManagedConnection trackByTx=" + trackConnectionByTx + " tx=" + trackByTransaction); 351 return super.getManagedConnection(trackByTransaction, subject, cri); 352 } 353 354 public void transactionStarted(Collection crs) throws SystemException 355 { 356 Set cls = new HashSet (); 357 for (Iterator i = crs.iterator(); i.hasNext(); ) 358 { 359 ConnectionRecord cr = (ConnectionRecord)i.next(); 360 ConnectionListener cl = cr.cl; 361 if (!cls.contains(cl)) 362 { 363 cls.add(cl); 364 cl.enlist(); 365 } 366 } 367 } 368 369 protected void managedConnectionReconnected(ConnectionListener cl) throws ResourceException 370 { 371 try 372 { 373 cl.enlist(); 374 } 375 catch (Throwable t) 376 { 377 if (trace) 378 log.trace("Could not enlist in transaction on entering meta-aware object! " + cl, t); 379 throw new JBossResourceException("Could not enlist in transaction on entering meta-aware object!", t); 380 } 381 } 382 383 protected void managedConnectionDisconnected(ConnectionListener cl) throws ResourceException 384 { 385 Throwable throwable = null; 386 try 387 { 388 cl.delist(); 389 } 390 catch (Throwable t) 391 { 392 throwable = t; 393 } 394 395 boolean isFree = cl.isManagedConnectionFree(); 397 if (trace) 398 log.trace("Disconnected isManagedConnectionFree=" + isFree + " cl=" + cl); 399 if (isFree) 400 returnManagedConnection(cl, false); 401 402 if (throwable != null) 404 JBossResourceException.rethrowAsResourceException("Could not delist resource, probably a transaction rollback? ", throwable); 405 } 406 407 public ConnectionListener createConnectionListener(ManagedConnection mc, Object context) 408 throws ResourceException 409 { 410 XAResource xaResource = null; 411 412 if (localTransactions) 413 { 414 xaResource = new LocalXAResource(log); 415 416 if (xaResourceTimeout != 0) 417 log.debug("XAResource transaction timeout cannot be set for local transactions: " + getJndiName()); 418 } 419 420 else 421 { 422 423 if(wrapXAResource) 424 { 425 log.trace("Generating XAResourceWrapper for TxConnectionManager" + this); 426 xaResource = new XAResourceWrapper(isSameRMOverrideValue, padXid, mc.getXAResource()); 427 428 } 429 430 else 431 { 432 log.trace("Not wrapping XAResource."); 433 xaResource = mc.getXAResource(); 434 435 } 436 437 if (xaResourceTimeout != 0) 438 { 439 try 440 { 441 if (xaResource.setTransactionTimeout(xaResourceTimeout) == false) 442 log.debug("XAResource does not support transaction timeout configuration: " + getJndiName()); 443 } 444 catch (XAException e) 445 { 446 throw new JBossResourceException("Unable to set XAResource transaction timeout: " + getJndiName(), e); 447 } 448 } 449 } 450 451 ConnectionListener cli = new TxConnectionEventListener(mc, poolingStrategy, context, log, xaResource); 452 mc.addConnectionEventListener(cli); 453 return cli; 454 } 455 456 public boolean isTransactional() 457 { 458 return TxUtils.isCompleted(tm) == false; 459 } 460 461 protected class TxConnectionEventListener 464 extends BaseConnectionManager2.BaseConnectionEventListener 465 { 466 467 protected Logger log; 468 469 protected TransactionSynchronization transactionSynchronization; 470 471 private final XAResource xaResource; 472 473 474 private SynchronizedBoolean localTransaction = new SynchronizedBoolean(false); 475 476 public TxConnectionEventListener(final ManagedConnection mc, final ManagedConnectionPool mcp, final Object context, Logger log, final XAResource xaResource) throws ResourceException 477 { 478 super(mc, mcp, context, log); 479 this.log = log; 480 this.xaResource = xaResource; 481 482 if (xaResource instanceof LocalXAResource) 483 ((LocalXAResource) xaResource).setConnectionListener(this); 484 } 485 486 public void enlist() throws SystemException 487 { 488 522 int status = tm.getStatus(); 524 if (status == Status.STATUS_NO_TRANSACTION) 525 { 526 if (transactionSynchronization != null && transactionSynchronization.currentTx != null) 527 { 528 String error = "Attempt to use connection outside a transaction when already a tx!"; 529 if (trace) 530 log.trace(error + " " + this); 531 throw new IllegalStateException (error); 532 } 533 if (trace) 534 log.trace("No transaction, no need to enlist: " + this); 535 return; 536 } 537 538 Transaction threadTx = tm.getTransaction(); 540 if (threadTx == null || status != Status.STATUS_ACTIVE) 541 { 542 String error = "Transaction " + threadTx + " is not active " + TxUtils.getStatusAsString(status); 543 if (trace) 544 log.trace(error + " cl=" + this); 545 throw new IllegalStateException (error); 546 } 547 548 if (trace) 549 log.trace("Pre-enlist: " + this + " threadTx=" + threadTx); 550 551 TransactionSynchronization ourSynchronization = null; 553 554 TransactionSynchronizer synchronizer = null; 557 558 TransactionSynchronizer.lock(threadTx); 559 try 560 { 561 if (isTrackByTx() == false && transactionSynchronization != null) 564 { 565 String error = "Can't enlist - already a tx!"; 566 if (trace) 567 log.trace(error + " " + this); 568 throw new IllegalStateException (error); 569 } 570 571 if (transactionSynchronization != null && transactionSynchronization.currentTx.equals(threadTx) == false) 573 { 574 String error = "Trying to change transaction " + threadTx + " in enlist!"; 575 if (trace) 576 log.trace(error +" " + this); 577 throw new IllegalStateException (error); 578 } 579 580 try 582 { 583 if (trace) 584 log.trace("Get synchronizer " + this + " threadTx=" + threadTx); 585 synchronizer = TransactionSynchronizer.getRegisteredSynchronizer(threadTx); 586 } 587 catch (Throwable t) 588 { 589 setTrackByTx(false); 590 rethrowAsSystemException("Cannot register synchronization", threadTx, t); 591 } 592 593 if (transactionSynchronization == null) 595 { 596 TransactionSynchronization synchronization = new TransactionSynchronization(threadTx, isTrackByTx()); 597 synchronizer.addUnenlisted(synchronization); 598 transactionSynchronization = synchronization; 599 } 600 ourSynchronization = transactionSynchronization; 601 } 602 finally 603 { 604 TransactionSynchronizer.unlock(threadTx); 605 } 606 607 ArrayList unenlisted = synchronizer.getUnenlisted(); 609 if (unenlisted != null) 610 { 611 try 612 { 613 for (int i = 0; i < unenlisted.size(); ++i) 614 { 615 TransactionSynchronization sync = (TransactionSynchronization) unenlisted.get(i); 616 if (sync.enlist()) 617 synchronizer.addEnlisted(sync); 618 } 619 } 620 finally 621 { 622 synchronizer.enlisted(); 623 } 624 } 625 626 if (trace) 628 log.trace("Check enlisted " + this + " threadTx=" + threadTx); 629 ourSynchronization.checkEnlisted(); 630 } 631 632 public void delist() throws ResourceException 633 { 634 if (trace) 635 log.trace("delisting " + this); 636 637 try 638 { 639 if (isTrackByTx() == false && transactionSynchronization != null) 640 { 641 Transaction tx = transactionSynchronization.currentTx; 642 TransactionSynchronization synchronization = transactionSynchronization; 643 transactionSynchronization = null; 644 if (TxUtils.isUncommitted(tx)) 645 { 646 TransactionSynchronizer synchronizer = TransactionSynchronizer.getRegisteredSynchronizer(tx); 647 if (synchronization.enlisted) 648 synchronizer.removeEnlisted(synchronization); 649 if (tx.delistResource(getXAResource(), XAResource.TMSUSPEND) == false) 650 throw new ResourceException ("Failure to delist resource: " + this); 651 } 652 } 653 } 654 catch (Throwable t) 655 { 656 JBossResourceException.rethrowAsResourceException("Error in delist!", t); 657 } 658 } 659 660 protected XAResource getXAResource() 662 { 663 return xaResource; 664 } 665 666 public void connectionClosed(ConnectionEvent ce) 667 { 668 if (trace) 669 log.trace("connectionClosed called mc=" + this.getManagedConnection()); 670 if (this.getManagedConnection() != (ManagedConnection )ce.getSource()) 671 throw new IllegalArgumentException ("ConnectionClosed event received from wrong ManagedConnection! Expected: " + this.getManagedConnection() + ", actual: " + ce.getSource()); 672 try 673 { 674 getCcm().unregisterConnection(TxConnectionManager.this, ce.getConnectionHandle()); 675 } 676 catch (Throwable t) 677 { 678 log.info("throwable from unregister connection", t); 679 } 680 681 try 682 { 683 unregisterAssociation(this, ce.getConnectionHandle()); 684 boolean isFree = isManagedConnectionFree(); 685 if (trace) 686 log.trace("isManagedConnectionFree=" + isFree + " mc=" + this.getManagedConnection()); 687 if (isFree) 689 { 690 delist(); 691 returnManagedConnection(this, false); 692 } 693 } 694 catch (Throwable t) 695 { 696 log.error("Error while closing connection handle!", t); 697 returnManagedConnection(this, true); 698 } 699 } 700 701 public void localTransactionStarted(ConnectionEvent ce) 702 { 703 localTransaction.set(true); 704 } 705 706 public void localTransactionCommitted(ConnectionEvent ce) 707 { 708 localTransaction.set(false); 709 } 710 711 public void localTransactionRolledback(ConnectionEvent ce) 712 { 713 localTransaction.set(false); 714 } 715 716 public void tidyup() throws ResourceException 717 { 718 if (localTransaction.get()) 720 { 721 LocalTransaction local = null; 722 ManagedConnection mc = getManagedConnection(); 723 try 724 { 725 local = mc.getLocalTransaction(); 726 } 727 catch (Throwable t) 728 { 729 JBossResourceException.rethrowAsResourceException("Unfinished local transaction - error getting local transaction from " + this, t); 730 } 731 if (local == null) 732 throw new ResourceException ("Unfinished local transaction but managed connection does not provide a local transaction. " + this); 733 else 734 { 735 local.rollback(); 736 log.debug("Unfinished local transaction was rolled back." + this); 737 } 738 } 739 } 740 741 public void connectionErrorOccurred(ConnectionEvent ce) 742 { 743 transactionSynchronization = null; 744 super.connectionErrorOccurred(ce); 745 } 746 747 public boolean isManagedConnectionFree() 749 { 750 if (isTrackByTx() && transactionSynchronization != null) 751 return false; 752 return super.isManagedConnectionFree(); 753 } 754 755 private class TransactionSynchronization implements Synchronization 756 { 757 758 private Transaction currentTx; 759 760 761 private boolean wasTrackByTx; 762 763 764 private boolean enlisted = false; 765 766 767 private Throwable enlistError; 768 769 774 public TransactionSynchronization(Transaction tx, boolean trackByTx) 775 { 776 this.currentTx = tx; 777 wasTrackByTx = trackByTx; 778 } 779 780 785 public void checkEnlisted() throws SystemException 786 { 787 if (enlistError != null) 788 { 789 String error = "Error enlisting resource in transaction=" + currentTx; 790 if (trace) 791 log.trace(error + " " + TxConnectionEventListener.this); 792 793 if (enlistError == FAILED_TO_ENLIST) 796 throw new SystemException (FAILED_TO_ENLIST + " tx=" + currentTx); 797 else 798 { 799 SystemException e = new SystemException (error); 800 e.initCause(enlistError); 801 throw e; 802 } 803 } 804 if (enlisted == false) 805 { 806 String error = "Resource is not enlisted in transaction=" + currentTx; 807 if (trace) 808 log.trace(error + " " + TxConnectionEventListener.this); 809 throw new IllegalStateException ("Resource was not enlisted."); 810 } 811 } 812 813 818 public boolean enlist() 819 { 820 if (trace) 821 log.trace("Enlisting resource " + TxConnectionEventListener.this); 822 try 823 { 824 XAResource resource = getXAResource(); 825 if (false == currentTx.enlistResource(resource)) 826 enlistError = FAILED_TO_ENLIST; 827 } 828 catch (Throwable t) 829 { 830 enlistError = t; 831 } 832 833 synchronized (this) 834 { 835 if (enlistError != null) 836 { 837 if (trace) 838 log.trace("Failed to enlist resource " + TxConnectionEventListener.this, enlistError); 839 setTrackByTx(false); 840 transactionSynchronization = null; 841 return false; 842 } 843 844 if (trace) 845 log.trace("Enlisted resource " + TxConnectionEventListener.this); 846 enlisted = true; 847 return true; 848 } 849 } 850 851 public void beforeCompletion() 852 { 853 } 854 855 public void afterCompletion(int status) 856 { 857 if (getState() == DESTROYED) 859 return; 860 861 if (this.equals(transactionSynchronization) == false) 863 { 864 if (wasTrackByTx == false) 866 return; 867 else 868 { 869 String message = "afterCompletion called with wrong tx! Expected: " + this + ", actual: " + transactionSynchronization; 871 IllegalStateException e = new IllegalStateException (message); 872 log.error("There is something wrong with the pooling?", e); 873 } 874 } 875 transactionSynchronization = null; 877 if (wasTrackByTx) 879 { 880 setTrackByTx(false); 881 if (isManagedConnectionFree()) 882 returnManagedConnection(TxConnectionEventListener.this, false); 883 } 884 } 885 886 public String toString() 887 { 888 StringBuffer buffer = new StringBuffer (); 889 buffer.append("TxSync").append(System.identityHashCode(this)); 890 buffer.append("{tx=").append(currentTx); 891 buffer.append(" wasTrackByTx=").append(wasTrackByTx); 892 buffer.append(" enlisted=").append(enlisted); 893 buffer.append("}"); 894 return buffer.toString(); 895 } 896 } 897 898 protected void toString(StringBuffer buffer) 900 { 901 buffer.append(" xaResource=").append(xaResource); 902 buffer.append(" txSync=").append(transactionSynchronization); 903 } 904 } 905 906 private class LocalXAResource implements XAResource , LastResource 907 { 908 protected Logger log; 909 910 private ConnectionListener cl; 911 912 917 private boolean warned = false; 918 919 private Xid currentXid; 920 921 public LocalXAResource(final Logger log) 922 { 923 this.log = log; 924 } 925 926 void setConnectionListener(ConnectionListener cl) 927 { 928 this.cl = cl; 929 } 930 931 933 public void start(Xid xid, int flags) throws XAException 934 { 935 if (trace) 936 log.trace("start, xid: " + xid + ", flags: " + flags); 937 if (currentXid != null && flags == XAResource.TMNOFLAGS) 938 throw new JBossLocalXAException("Trying to start a new tx when old is not complete! old: " + currentXid + ", new " + xid + ", flags " + flags, XAException.XAER_PROTO); 939 if (currentXid == null && flags != XAResource.TMNOFLAGS) 940 throw new JBossLocalXAException("Trying to start a new tx with wrong flags! new " + xid + ", flags " + flags, XAException.XAER_PROTO); 941 if (currentXid == null) 942 { 943 try 944 { 945 cl.getManagedConnection().getLocalTransaction().begin(); 946 } 947 catch (ResourceException re) 948 { 949 throw new JBossLocalXAException("Error trying to start local tx: ", XAException.XAER_RMERR, re); 950 } 951 catch (Throwable t) 952 { 953 throw new JBossLocalXAException("Throwable trying to start local transaction!", XAException.XAER_RMERR, t); 954 } 955 956 currentXid = xid; 957 } 958 } 959 960 public void end(Xid xid, int flags) throws XAException 961 { 962 if (trace) 963 log.trace("end on xid: " + xid + " called with flags " + flags); 964 } 965 966 public void commit(Xid xid, boolean onePhase) throws XAException 967 { 968 if (xid.equals(currentXid) == false) 969 throw new JBossLocalXAException("wrong xid in commit: expected: " + currentXid + ", got: " + xid, XAException.XAER_PROTO); 970 currentXid = null; 971 try 972 { 973 cl.getManagedConnection().getLocalTransaction().commit(); 974 } 975 catch (ResourceException re) 976 { 977 returnManagedConnection(cl, true); 978 if (trace) 979 log.trace("commit problem: ", re); 980 throw new JBossLocalXAException("could not commit local tx", XAException.XA_RBROLLBACK, re); 981 } 982 } 983 984 public void forget(Xid xid) throws XAException 985 { 986 throw new JBossLocalXAException("forget not supported in local tx", XAException.XAER_RMERR); 987 } 988 989 public int getTransactionTimeout() throws XAException 990 { 991 return 0; 993 } 994 995 public boolean isSameRM(XAResource xaResource) throws XAException 996 { 997 return xaResource == this; 998 } 999 1000 public int prepare(Xid xid) throws XAException 1001 { 1002 if (!warned) 1003 log.warn("Prepare called on a local tx. Use of local transactions on a jta transaction with more than one branch may result in inconsistent data in some cases of failure."); 1004 warned = true; 1005 return XAResource.XA_OK; 1006 } 1007 1008 public Xid [] recover(int flag) throws XAException 1009 { 1010 throw new JBossLocalXAException("no recover with local-tx only resource managers", XAException.XAER_RMERR); 1011 } 1012 1013 public void rollback(Xid xid) throws XAException 1014 { 1015 if (xid.equals(currentXid) == false) 1016 throw new JBossLocalXAException("wrong xid in rollback: expected: " + currentXid + ", got: " + xid, XAException.XAER_PROTO); 1017 currentXid = null; 1018 try 1019 { 1020 cl.getManagedConnection().getLocalTransaction().rollback(); 1021 } 1022 catch (ResourceException re) 1023 { 1024 returnManagedConnection(cl, true); 1025 if (trace) 1026 log.trace("rollback problem: ", re); 1027 throw new JBossLocalXAException("could not rollback local tx", XAException.XAER_RMERR, re); 1028 } 1029 } 1030 1031 public boolean setTransactionTimeout(int seconds) throws XAException 1032 { 1033 return false; 1035 } 1036 } 1037} 1038 | Popular Tags |