KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > resource > connectionmanager > TxConnectionManager


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.resource.connectionmanager;
23
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.Set JavaDoc;
29
30 import javax.management.ObjectName JavaDoc;
31 import javax.naming.InitialContext JavaDoc;
32 import javax.resource.ResourceException JavaDoc;
33 import javax.resource.spi.ConnectionEvent JavaDoc;
34 import javax.resource.spi.ConnectionRequestInfo JavaDoc;
35 import javax.resource.spi.LocalTransaction JavaDoc;
36 import javax.resource.spi.ManagedConnection JavaDoc;
37 import javax.security.auth.Subject JavaDoc;
38 import javax.transaction.RollbackException JavaDoc;
39 import javax.transaction.Status JavaDoc;
40 import javax.transaction.Synchronization JavaDoc;
41 import javax.transaction.SystemException JavaDoc;
42 import javax.transaction.Transaction JavaDoc;
43 import javax.transaction.TransactionManager JavaDoc;
44 import javax.transaction.xa.XAException JavaDoc;
45 import javax.transaction.xa.XAResource JavaDoc;
46 import javax.transaction.xa.Xid JavaDoc;
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 /**
59  * The TxConnectionManager is a JBoss ConnectionManager
60  * implementation for jca adapters implementing LocalTransaction and XAResource support.
61  *
62  * It implements a ConnectionEventListener that implements XAResource to
63  * manage transactions through the Transaction Manager. To assure that all
64  * work in a local transaction occurs over the same ManagedConnection, it
65  * includes a xid to ManagedConnection map. When a Connection is requested
66  * or a transaction started with a connection handle in use, it checks to
67  * see if a ManagedConnection already exists enrolled in the global
68  * transaction and uses it if found. Otherwise a free ManagedConnection
69  * has its LocalTransaction started and is used. From the
70  * BaseConnectionManager2, it includes functionality to obtain managed
71  * connections from
72  * a ManagedConnectionPool mbean, find the Subject from a SubjectSecurityDomain,
73  * and interact with the CachedConnectionManager for connections held over
74  * transaction and method boundaries. Important mbean references are to a
75  * ManagedConnectionPool supplier (typically a JBossManagedConnectionPool), and a
76  * RARDeployment representing the ManagedConnectionFactory.
77  *
78  * This connection manager has to perform the following operations:
79  *
80  * 1. When an application component requests a new ConnectionHandle,
81  * it must find a ManagedConnection, and make sure a
82  * ConnectionEventListener is registered. It must inform the
83  * CachedConnectionManager that a connection handle has been given
84  * out. It needs to count the number of handles for each
85  * ManagedConnection. If there is a current transaction, it must
86  * enlist the ManagedConnection's LocalTransaction in the transaction
87  * using the ConnectionEventListeners XAResource XAResource implementation.
88  * Entry point: ConnectionManager.allocateConnection.
89  * written.
90  *
91  * 2. When a ConnectionClosed event is received from the
92  * ConnectionEventListener, it must reduce the handle count. If
93  * the handle count is zero, the XAResource should be delisted from
94  * the Transaction, if any. The CachedConnectionManager must be
95  * notified that the connection is closed.
96  * Entry point: ConnectionEventListener.ConnectionClosed.
97  * written
98  *
99  *3. When a transaction begun notification is received from the
100  * UserTransaction (via the CachedConnectionManager, all
101  * managedConnections associated with the current object must be
102  * enlisted in the transaction.
103  * Entry point: (from
104  * CachedConnectionManager)
105  * ConnectionCacheListener.transactionStarted(Transaction,
106  * Collection). The collection is of ConnectionRecord objects.
107  * written.
108  *
109  * 5. When an "entering object" notification is received from the
110  * CachedConnectionInterceptor, all the connections for the current
111  * object must be associated with a ManagedConnection. if there is a
112  * Transaction, the XAResource must be enlisted with it.
113  * Entry point: ConnectionCacheListener.reconnect(Collection conns) The Collection
114  * is of ConnectionRecord objects.
115  * written.
116  *
117  * 6. When a "leaving object" notification is received from the
118  * CachedConnectionInterceptor, all the managedConnections for the
119  * current object must have their XAResources delisted from the
120  * current Transaction, if any, and cleanup called on each
121  * ManagedConnection.
122  * Entry point: ConnectionCacheListener.disconnect(Collection conns).
123  * written.
124  *
125  * @author <a HREF="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
126  * @author <a HREF="mailto:adrian@jboss.org">Adrian Brock</a>
127  * @author <a HREF="weston.price@jboss.com">Weston Price</a>
128  * @version $Revision: 57120 $
129  */

130 public class TxConnectionManager extends BaseConnectionManager2 implements TxConnectionManagerMBean
131 {
132    private static final Throwable JavaDoc FAILED_TO_ENLIST = new Throwable JavaDoc("Unabled to enlist resource, see the previous warnings.");
133
134    private ObjectName JavaDoc transactionManagerService;
135    
136    //use the object name, please
137
private String JavaDoc tmName;
138
139    private TransactionManager JavaDoc 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 JavaDoc isSameRMOverrideValue;
152    
153    protected static void rethrowAsSystemException(String JavaDoc context, Transaction JavaDoc tx, Throwable JavaDoc t)
154       throws SystemException JavaDoc
155    {
156       if (t instanceof SystemException JavaDoc)
157          throw (SystemException JavaDoc) t;
158       if (t instanceof RuntimeException JavaDoc)
159          throw (RuntimeException JavaDoc) t;
160       if (t instanceof Error JavaDoc)
161          throw (Error JavaDoc) t;
162       if (t instanceof RollbackException JavaDoc)
163          throw new IllegalStateException JavaDoc(context + " tx=" + tx + " marked for rollback.");
164       throw new NestedRuntimeException(context + " tx=" + tx + " got unexpected error ", t);
165    }
166    
167    /**
168     * Default managed TxConnectionManager constructor for mbean instances.
169     */

170    public TxConnectionManager()
171    {
172    }
173
174    /**
175     * Creates a new <code>TxConnectionManager</code> instance.
176     * for TESTING ONLY!!! not a managed constructor!!
177     *
178     * @param ccm a <code>CachedConnectionManager</code> value
179     * @param poolingStrategy a <code>ManagedConnectionPool</code> value
180     * @param tm a <code>TransactionManager</code> value
181     */

182    public TxConnectionManager (final CachedConnectionManager ccm,
183                                final ManagedConnectionPool poolingStrategy,
184                                final TransactionManager JavaDoc tm)
185    {
186       super(ccm, poolingStrategy);
187       this.tm = tm;
188    }
189
190    public ObjectName JavaDoc getTransactionManagerService()
191    {
192       return transactionManagerService;
193    }
194
195    public void setTransactionManagerService(ObjectName JavaDoc transactionManagerService)
196    {
197       this.transactionManagerService = transactionManagerService;
198    }
199
200    /**
201     * @deprecated
202     */

203    public void setTransactionManager(final String JavaDoc tmName)
204    {
205       this.tmName = tmName;
206    }
207
208    /**
209     * @deprecated
210     */

211    public String JavaDoc getTransactionManager()
212    {
213       return this.tmName;
214    }
215
216    public TransactionManager JavaDoc getTransactionManagerInstance()
217    {
218       return tm;
219    }
220
221    public void setTransactionManagerInstance(TransactionManager JavaDoc 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    /**
257     * Get the IsSameRMOverrideValue value.
258     *
259     * @return the IsSameRMOverrideValue value.
260     */

261    public Boolean JavaDoc 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    /**
288     * Set the IsSameRMOverrideValue value.
289     *
290     * @param isSameRMOverrideValue The new IsSameRMOverrideValue value.
291     */

292    public void setIsSameRMOverrideValue(Boolean JavaDoc isSameRMOverrideValue)
293    {
294       this.isSameRMOverrideValue = isSameRMOverrideValue;
295    }
296    
297    public long getTimeLeftBeforeTransactionTimeout(boolean errorRollback) throws RollbackException JavaDoc
298    {
299       if (tm == null)
300          throw new IllegalStateException JavaDoc("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 JavaDoc
307    {
308       if (transactionManagerService != null)
309          tm = (TransactionManager JavaDoc)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 JavaDoc)new InitialContext JavaDoc().lookup(tmName);
320       }
321       
322       
323       super.startService();
324    }
325
326    protected void stopService() throws Exception JavaDoc
327    {
328       this.tm = null;
329       super.stopService();
330    }
331
332    public ConnectionListener getManagedConnection(Subject JavaDoc subject, ConnectionRequestInfo JavaDoc cri)
333       throws ResourceException JavaDoc
334    {
335       Transaction JavaDoc trackByTransaction = null;
336       try
337       {
338          Transaction JavaDoc tx = tm.getTransaction();
339          if (tx != null && TxUtils.isActive(tx) == false)
340             throw new ResourceException JavaDoc("Transaction is not active: tx=" + tx);
341          if (trackConnectionByTx)
342             trackByTransaction = tx;
343       }
344       catch (Throwable JavaDoc 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 JavaDoc crs) throws SystemException JavaDoc
355    {
356       Set JavaDoc cls = new HashSet JavaDoc();
357       for (Iterator JavaDoc 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 JavaDoc
370    {
371       try
372       {
373          cl.enlist();
374       }
375       catch (Throwable JavaDoc 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 JavaDoc
384    {
385       Throwable JavaDoc throwable = null;
386       try
387       {
388          cl.delist();
389       }
390       catch (Throwable JavaDoc t)
391       {
392          throwable = t;
393       }
394
395       //if there are no more handles and tx is complete, we can return to pool.
396
boolean isFree = cl.isManagedConnectionFree();
397       if (trace)
398          log.trace("Disconnected isManagedConnectionFree=" + isFree + " cl=" + cl);
399       if (isFree)
400          returnManagedConnection(cl, false);
401
402       // Rethrow the error
403
if (throwable != null)
404          JBossResourceException.rethrowAsResourceException("Could not delist resource, probably a transaction rollback? ", throwable);
405    }
406
407    public ConnectionListener createConnectionListener(ManagedConnection JavaDoc mc, Object JavaDoc context)
408       throws ResourceException JavaDoc
409    {
410       XAResource JavaDoc 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 JavaDoc 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    // implementation of javax.resource.spi.ConnectionEventListener interface
462
//there is one of these for each ManagedConnection instance. It lives as long as the ManagedConnection.
463
protected class TxConnectionEventListener
464       extends BaseConnectionManager2.BaseConnectionEventListener
465    {
466       /** Use our own logger to prevent MNFE caused by compiler bug with nested classes. */
467       protected Logger log;
468
469       protected TransactionSynchronization transactionSynchronization;
470       
471       private final XAResource JavaDoc xaResource;
472
473       /** Whether there is a local transaction */
474       private SynchronizedBoolean localTransaction = new SynchronizedBoolean(false);
475       
476       public TxConnectionEventListener(final ManagedConnection JavaDoc mc, final ManagedConnectionPool mcp, final Object JavaDoc context, Logger log, final XAResource JavaDoc xaResource) throws ResourceException JavaDoc
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 JavaDoc
487       {
488          // This method is a bit convulted, but it has to be such because
489
// there is a race condition in the transaction manager where it
490
// unlocks during the enlist of the XAResource. It does this
491
// to avoid distributed deadlocks and to ensure the transaction
492
// timeout can fail a badly behaving resource during the enlist.
493
//
494
// When two threads in the same transaction are trying to enlist
495
// connections they could be from the same resource manager
496
// or even the same connection when tracking the connection by transaction.
497
//
498
// For the same connection, we only want to do the real enlist once.
499
// For two connections from the same resource manager we don't
500
// want the join before the initial start request.
501
//
502
// The solution is to build up a list of unenlisted resources
503
// in the TransactionSynchronizer and then choose one of the
504
// threads that is contending in the transaction to enlist them
505
// in order. The actual order doesn't really matter as it is the
506
// transaction manager that calculates the enlist flags and determines
507
// whether the XAResource was already enlisted.
508
//
509
// Once there are no unenlisted resources the threads are released
510
// to return the result of the enlistments.
511
//
512
// In practice, a thread just takes a snapshot to try to avoid one
513
// thread having to do all the work. If it did not do them all
514
// the next waiting thread will do the next snapshot until there
515
// there is either no snapshot or no waiting threads.
516
//
517
// A downside to this design is a thread could have its resource enlisted by
518
// an earlier thread while it enlists some later thread's resource.
519
// Since they are all a part of the same transaction, this is probably
520
// not a real issue.
521

522          // No transaction associated with the thread
523
int status = tm.getStatus();
524          if (status == Status.STATUS_NO_TRANSACTION)
525          {
526             if (transactionSynchronization != null && transactionSynchronization.currentTx != null)
527             {
528                String JavaDoc error = "Attempt to use connection outside a transaction when already a tx!";
529                if (trace)
530                   log.trace(error + " " + this);
531                throw new IllegalStateException JavaDoc(error);
532             }
533             if (trace)
534                log.trace("No transaction, no need to enlist: " + this);
535             return;
536          }
537          
538          // Inactive transaction
539
Transaction JavaDoc threadTx = tm.getTransaction();
540          if (threadTx == null || status != Status.STATUS_ACTIVE)
541          {
542             String JavaDoc error = "Transaction " + threadTx + " is not active " + TxUtils.getStatusAsString(status);
543             if (trace)
544                log.trace(error + " cl=" + this);
545             throw new IllegalStateException JavaDoc(error);
546          }
547
548          if (trace)
549             log.trace("Pre-enlist: " + this + " threadTx=" + threadTx);
550          
551          // Our synchronization
552
TransactionSynchronization ourSynchronization = null;
553
554          // Serializes enlistment when two different threads are enlisting
555
// different connections in the same transaction concurrently
556
TransactionSynchronizer synchronizer = null;
557
558          TransactionSynchronizer.lock(threadTx);
559          try
560          {
561             // Interleaving should have an unenlisted transaction
562
// TODO: We should be able to do some sharing shouldn't we?
563
if (isTrackByTx() == false && transactionSynchronization != null)
564             {
565                String JavaDoc error = "Can't enlist - already a tx!";
566                if (trace)
567                   log.trace(error + " " + this);
568                throw new IllegalStateException JavaDoc(error);
569             }
570             
571             // Check for different transaction
572
if (transactionSynchronization != null && transactionSynchronization.currentTx.equals(threadTx) == false)
573             {
574                String JavaDoc error = "Trying to change transaction " + threadTx + " in enlist!";
575                if (trace)
576                   log.trace(error +" " + this);
577                throw new IllegalStateException JavaDoc(error);
578             }
579
580             // Get the synchronizer
581
try
582             {
583                if (trace)
584                   log.trace("Get synchronizer " + this + " threadTx=" + threadTx);
585                synchronizer = TransactionSynchronizer.getRegisteredSynchronizer(threadTx);
586             }
587             catch (Throwable JavaDoc t)
588             {
589                setTrackByTx(false);
590                rethrowAsSystemException("Cannot register synchronization", threadTx, t);
591             }
592
593             // First time through, create a transaction synchronization
594
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          // Perform the enlistment(s)
608
ArrayList JavaDoc 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          // What was the result of our enlistment?
627
if (trace)
628             log.trace("Check enlisted " + this + " threadTx=" + threadTx);
629          ourSynchronization.checkEnlisted();
630       }
631
632       public void delist() throws ResourceException JavaDoc
633       {
634          if (trace)
635             log.trace("delisting " + this);
636
637          try
638          {
639             if (isTrackByTx() == false && transactionSynchronization != null)
640             {
641                Transaction JavaDoc 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 JavaDoc("Failure to delist resource: " + this);
651                }
652             }
653          }
654          catch (Throwable JavaDoc t)
655          {
656             JBossResourceException.rethrowAsResourceException("Error in delist!", t);
657          }
658       }
659
660       //local will return this, xa will return one from mc.
661
protected XAResource JavaDoc getXAResource()
662       {
663          return xaResource;
664       }
665
666       public void connectionClosed(ConnectionEvent JavaDoc ce)
667       {
668          if (trace)
669             log.trace("connectionClosed called mc=" + this.getManagedConnection());
670          if (this.getManagedConnection() != (ManagedConnection JavaDoc)ce.getSource())
671             throw new IllegalArgumentException JavaDoc("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 JavaDoc 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             //no more handles
688
if (isFree)
689             {
690                delist();
691                returnManagedConnection(this, false);
692             }
693          }
694          catch (Throwable JavaDoc t)
695          {
696             log.error("Error while closing connection handle!", t);
697             returnManagedConnection(this, true);
698          }
699       }
700
701       public void localTransactionStarted(ConnectionEvent JavaDoc ce)
702       {
703          localTransaction.set(true);
704       }
705
706       public void localTransactionCommitted(ConnectionEvent JavaDoc ce)
707       {
708          localTransaction.set(false);
709       }
710
711       public void localTransactionRolledback(ConnectionEvent JavaDoc ce)
712       {
713          localTransaction.set(false);
714       }
715
716       public void tidyup() throws ResourceException JavaDoc
717       {
718          // We have a hanging transaction
719
if (localTransaction.get())
720          {
721             LocalTransaction JavaDoc local = null;
722             ManagedConnection JavaDoc mc = getManagedConnection();
723             try
724             {
725                local = mc.getLocalTransaction();
726             }
727             catch (Throwable JavaDoc t)
728             {
729                JBossResourceException.rethrowAsResourceException("Unfinished local transaction - error getting local transaction from " + this, t);
730             }
731             if (local == null)
732                throw new ResourceException JavaDoc("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 JavaDoc ce)
742       {
743          transactionSynchronization = null;
744          super.connectionErrorOccurred(ce);
745       }
746
747       //Important method!!
748
public boolean isManagedConnectionFree()
749       {
750          if (isTrackByTx() && transactionSynchronization != null)
751             return false;
752          return super.isManagedConnectionFree();
753       }
754
755       private class TransactionSynchronization implements Synchronization JavaDoc
756       {
757          /** Transaction */
758          private Transaction JavaDoc currentTx;
759          
760          /** This is the status when we were registered */
761          private boolean wasTrackByTx;
762
763          /** Whether we are enlisted */
764          private boolean enlisted = false;
765          
766          /** Any error during enlistment */
767          private Throwable JavaDoc enlistError;
768          
769          /**
770           * Create a new TransactionSynchronization.
771           *
772           * @param trackByTx whether this is track by connection
773           */

774          public TransactionSynchronization(Transaction JavaDoc tx, boolean trackByTx)
775          {
776             this.currentTx = tx;
777             wasTrackByTx = trackByTx;
778          }
779          
780          /**
781           * Get the result of the enlistment
782           *
783           * @throws SystemExeption for any error
784           */

785          public void checkEnlisted() throws SystemException JavaDoc
786          {
787             if (enlistError != null)
788             {
789                String JavaDoc error = "Error enlisting resource in transaction=" + currentTx;
790                if (trace)
791                   log.trace(error + " " + TxConnectionEventListener.this);
792
793                // Wrap the error to give a reasonable stacktrace since the resource
794
// could have been enlisted by a different thread
795
if (enlistError == FAILED_TO_ENLIST)
796                   throw new SystemException JavaDoc(FAILED_TO_ENLIST + " tx=" + currentTx);
797                else
798                {
799                   SystemException JavaDoc e = new SystemException JavaDoc(error);
800                   e.initCause(enlistError);
801                   throw e;
802                }
803             }
804             if (enlisted == false)
805             {
806                String JavaDoc error = "Resource is not enlisted in transaction=" + currentTx;
807                if (trace)
808                   log.trace(error + " " + TxConnectionEventListener.this);
809                throw new IllegalStateException JavaDoc("Resource was not enlisted.");
810             }
811          }
812          
813          /**
814           * Enlist the resource
815           *
816           * @return true when enlisted, false otherwise
817           */

818          public boolean enlist()
819          {
820             if (trace)
821                log.trace("Enlisting resource " + TxConnectionEventListener.this);
822             try
823             {
824                XAResource JavaDoc resource = getXAResource();
825                if (false == currentTx.enlistResource(resource))
826                   enlistError = FAILED_TO_ENLIST;
827             }
828             catch (Throwable JavaDoc 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             // The connection got destroyed during the transaction
858
if (getState() == DESTROYED)
859                return;
860             
861             // Are we still in the original transaction?
862
if (this.equals(transactionSynchronization) == false)
863             {
864                // If we are interleaving transactions we have nothing to do
865
if (wasTrackByTx == false)
866                   return;
867                else
868                {
869                   // There is something wrong with the pooling
870
String JavaDoc message = "afterCompletion called with wrong tx! Expected: " + this + ", actual: " + transactionSynchronization;
871                   IllegalStateException JavaDoc e = new IllegalStateException JavaDoc(message);
872                   log.error("There is something wrong with the pooling?", e);
873                }
874             }
875             // "Delist"
876
transactionSynchronization = null;
877             // This is where we close when doing track by transaction
878
if (wasTrackByTx)
879             {
880                setTrackByTx(false);
881                if (isManagedConnectionFree())
882                   returnManagedConnection(TxConnectionEventListener.this, false);
883             }
884          }
885          
886          public String JavaDoc toString()
887          {
888             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
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       // For debugging
899
protected void toString(StringBuffer JavaDoc buffer)
900       {
901          buffer.append(" xaResource=").append(xaResource);
902          buffer.append(" txSync=").append(transactionSynchronization);
903       }
904    }
905
906    private class LocalXAResource implements XAResource JavaDoc, LastResource
907    {
908       protected Logger log;
909
910       private ConnectionListener cl;
911
912       /**
913        * <code>warned</code> is set after one warning about a local participant
914        * in a multi-branch jta transaction is logged.
915        *
916        */

917       private boolean warned = false;
918
919       private Xid JavaDoc 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       // implementation of javax.transaction.xa.XAResource interface
932

933       public void start(Xid JavaDoc xid, int flags) throws XAException JavaDoc
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 JavaDoc re)
948             {
949                throw new JBossLocalXAException("Error trying to start local tx: ", XAException.XAER_RMERR, re);
950             }
951             catch (Throwable JavaDoc 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 JavaDoc xid, int flags) throws XAException JavaDoc
961       {
962          if (trace)
963             log.trace("end on xid: " + xid + " called with flags " + flags);
964       }
965
966       public void commit(Xid JavaDoc xid, boolean onePhase) throws XAException JavaDoc
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 JavaDoc 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 JavaDoc xid) throws XAException JavaDoc
985       {
986          throw new JBossLocalXAException("forget not supported in local tx", XAException.XAER_RMERR);
987       }
988
989       public int getTransactionTimeout() throws XAException JavaDoc
990       {
991          // TODO: implement this javax.transaction.xa.XAResource method
992
return 0;
993       }
994
995       public boolean isSameRM(XAResource JavaDoc xaResource) throws XAException JavaDoc
996       {
997          return xaResource == this;
998       }
999
1000      public int prepare(Xid JavaDoc xid) throws XAException JavaDoc
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 JavaDoc[] recover(int flag) throws XAException JavaDoc
1009      {
1010         throw new JBossLocalXAException("no recover with local-tx only resource managers", XAException.XAER_RMERR);
1011      }
1012
1013      public void rollback(Xid JavaDoc xid) throws XAException JavaDoc
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 JavaDoc 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 JavaDoc
1032      {
1033         // TODO: implement this javax.transaction.xa.XAResource method
1034
return false;
1035      }
1036   }
1037}
1038
Popular Tags