KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > jotm > Current


1 /*
2  * @(#) Current.java
3  *
4  * JOTM: Java Open Transaction Manager
5  *
6  *
7  * This module was originally developed by
8  *
9  * - Bull S.A. as part of the JOnAS application server code released in
10  * July 1999 (www.bull.com)
11  * --------------------------------------------------------------------------
12  * The original code and portions created by Bull SA are
13  * Copyright (c) 1999 BULL SA
14  * All rights reserved.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions are met:
18  *
19  * -Redistributions of source code must retain the above copyright notice, this
20  * list of conditions and the following disclaimer.
21  *
22  * -Redistributions in binary form must reproduce the above copyright notice,
23  * this list of conditions and the following disclaimer in the documentation
24  * and/or other materials provided with the distribution.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  * --------------------------------------------------------------------------
38  *
39  * Contributor(s):
40  * 01/11/06 Christophe Ney cney@batisseurs.com
41  * Added ResourceManagerListener mechanism to remove ThreadData
42  * dependency.
43  * 01/12/03 Dean Jennings - synchronizedMap for txXids
44  *
45  * --------------------------------------------------------------------------
46  * $Id: Current.java,v 1.54 2005/05/12 14:06:26 tonyortiz Exp $
47  * --------------------------------------------------------------------------
48  */

49 package org.objectweb.jotm;
50
51 import java.io.Serializable JavaDoc;
52 import java.util.Collections JavaDoc;
53 import java.util.Date JavaDoc;
54 import java.util.EmptyStackException JavaDoc;
55 import java.util.HashMap JavaDoc;
56 import java.util.Iterator JavaDoc;
57 import java.util.List JavaDoc;
58 import java.util.Map JavaDoc;
59 import java.util.Set JavaDoc;
60 import java.util.Stack JavaDoc;
61 import java.util.Vector JavaDoc;
62
63 import javax.naming.NamingException JavaDoc;
64 import javax.naming.Reference JavaDoc;
65 import javax.naming.Referenceable JavaDoc;
66 import javax.naming.StringRefAddr JavaDoc;
67 import javax.resource.spi.XATerminator JavaDoc;
68 import javax.transaction.HeuristicMixedException JavaDoc;
69 import javax.transaction.HeuristicRollbackException JavaDoc;
70 import javax.transaction.InvalidTransactionException JavaDoc;
71 import javax.transaction.NotSupportedException JavaDoc;
72 import javax.transaction.RollbackException JavaDoc;
73 import javax.transaction.Status JavaDoc;
74 import javax.transaction.SystemException JavaDoc;
75 import javax.transaction.Transaction JavaDoc;
76 import javax.transaction.UserTransaction JavaDoc;
77 import javax.transaction.xa.XAException JavaDoc;
78 import javax.transaction.xa.XAResource JavaDoc;
79
80 import org.objectweb.transaction.jta.ResourceManagerEvent;
81 import org.objectweb.transaction.jta.TransactionManager;
82 import org.objectweb.howl.log.xa.XACommittingTx;
83
84 /**
85  * <code>Current</code> is the common implementation for both
86  * <code>UserTransaction</code> and <code>TransactionManager</code>.
87  * <ul>
88  * <li><code>UserTransaction</code> is used by clients that want to demarcate
89  * transactions themselves. It is referenceable through JNDI.</li>
90  * <li><code>TransactionManager</code> is used by an application server.</li>
91  * </ul>
92  *
93  * <p>This object is unique in a VM, i. e. each application server has
94  * <em>ONE</em> <code>Current</code> object and each client program should
95  * normally issue only <em>ONE</em> lookup on JNDI.</p>
96  *
97  * <p><code>Current</code> also implements <code>Referenceable</code> and
98  * <code>Serializable</code> because of JNDI.</p>
99  */

100
101 public class Current implements UserTransaction JavaDoc, TransactionManager, Referenceable JavaDoc, Serializable JavaDoc {
102
103     // transaction associated to the thread
104
// Thread specific data
105
private transient static ThreadLocal JavaDoc threadTx = new ThreadLocal JavaDoc();
106
107     // ------------------------------------------------------------------
108
// Private data
109
// ------------------------------------------------------------------
110

111     // Static hashtable: Xid ---> transaction
112
private transient static Map JavaDoc txXids = Collections.synchronizedMap(new HashMap JavaDoc());
113
114     // Must be init at null, for clients that do not get UserTransaction
115
private transient static Current unique = null;
116     
117     // private info
118
private transient static TimerManager timermgr = null; // local
119
private transient static TransactionFactory tm = null; // local or remote
120
private transient static TransactionRecovery tr = null; // transaction recovery object
121

122     private static final String JavaDoc JOTM_VERSION = "JOTM 2.0.10";
123     private static final int DEFAULT_TIMEOUT = 60;
124     private int defaultTimeout = DEFAULT_TIMEOUT;
125     private int transactionTimeout = DEFAULT_TIMEOUT;
126     private static final boolean DEFAULT_RECOVERY = false;
127     private static boolean transactionRecovery = DEFAULT_RECOVERY;
128     
129     // management counter
130
private transient int nb_bg_tx = 0;
131     private transient int nb_rb_tx = 0;
132     private transient int nb_cm_tx = 0;
133     private transient int nb_to = 0;
134
135     // ------------------------------------------------------------------
136
// Constructors
137
// ------------------------------------------------------------------
138

139     /**
140      * Default constructor.
141      * A client does not need the TMFactory.
142      */

143
144     public Current(){
145         TraceTm.jta.info(JOTM_VERSION);
146         
147         if (TraceTm.jta.isDebugEnabled()) {
148             TraceTm.jta.debug("no args constructor");
149         }
150
151         unique = this;
152         timermgr = TimerManager.getInstance();
153         
154         try {
155             tr = new TransactionRecoveryImpl ();
156         } catch (Exception JavaDoc e) {
157             setDefaultRecovery(false);
158             TraceTm.recovery.error("Cannot open Howl Log");
159             TraceTm.recovery.error("JOTM Recovery is being disabled");
160         }
161     }
162
163     /**
164      * Constructor for an application server.
165      *
166      * The TM factory is passed as an argument. Note that the TM factory can be
167      * either local or remote.
168      *
169      * @param tmfact TM Factory to use
170      */

171
172     public Current(TransactionFactory tmfact){
173         TraceTm.jta.info(JOTM_VERSION);
174         
175         if (TraceTm.jta.isDebugEnabled()) {
176             TraceTm.jta.debug("TransactionFactory="+ tmfact);
177         }
178
179         unique = this;
180         tm = tmfact;
181         
182         timermgr = TimerManager.getInstance();
183         
184         try {
185             tr = new TransactionRecoveryImpl ();
186         } catch (Exception JavaDoc e) {
187             setDefaultRecovery(false);
188             TraceTm.recovery.error("Cannot open Howl Log");
189             TraceTm.recovery.error("JOTM Recovery is being disabled");
190         }
191     }
192
193     /**
194      * Gets the <code>TransactionManager</code> instance.
195      *
196      * @return TransactionManager
197      */

198
199     public static TransactionManager getTransactionManager() {
200         return (TransactionManager) unique;
201     }
202
203     // ------------------------------------------------------------------
204
// UserTransaction implementation
205
// ------------------------------------------------------------------
206

207     /**
208      * Creates a new transaction and associate it with the current thread.
209      *
210      * @exception NotSupportedException Thrown if the thread is already
211      * associated with a transaction. (nested transaction are not
212      * supported)
213      *
214      * @exception SystemException Thrown if the transaction manager
215      * encounters an unexpected error condition
216      */

217
218     public void begin() throws NotSupportedException JavaDoc, SystemException JavaDoc {
219
220         if (TraceTm.jta.isDebugEnabled()) {
221             TraceTm.jta.debug("begin transaction");
222         }
223
224         // checks that no transaction is already associated with this thread.
225
TransactionImpl tx = (TransactionImpl) threadTx.get();
226
227         if (TraceTm.jta.isDebugEnabled()) {
228             TraceTm.jta.debug("threadTx.get= " + threadTx.toString());
229         }
230
231         if (tx != null) {
232             if (txXids.containsValue(tx)) {
233                 throw new NotSupportedException JavaDoc("Nested transactions not supported");
234             } else {
235                 if (TraceTm.jta.isDebugEnabled()) {
236                     TraceTm.jta.debug("Resetting current tx = " + tx + " since it is already completed.");
237                 }
238             }
239         }
240
241         // builds a new Xid
242
// - should pass servername + ip addr. (LATER)
243
XidImpl otid = new XidImpl();
244
245         // creates a new TransactionImpl object
246
// - May raise SystemException
247
tx = new TransactionImpl(otid, transactionTimeout);
248
249         if (TraceTm.jta.isDebugEnabled()) {
250             TraceTm.jta.debug("tx=" + tx);
251         }
252
253         /// clear suspended transaction.
254
try {
255             tx.doAttach(XAResource.TMJOIN);
256         } catch (RollbackException JavaDoc e) {
257             // never.
258
TraceTm.jotm.error("doAttach: RollbackException");
259             throw new SystemException JavaDoc("RollbackException in occured in begin() " + e.getMessage());
260         }
261
262         // associates transaction with current thread
263
threadTx.set(tx);
264
265         if (TraceTm.jta.isDebugEnabled()) {
266             TraceTm.jta.debug("threadTx.set= " + threadTx.toString());
267         }
268
269         // associates this Tx with the Xid
270
putTxXid(otid, tx);
271
272         // sets a timer for the transaction
273
if (timermgr != null) {
274             tx.setTimer(timermgr.addTimer(tx, transactionTimeout, null, false));
275         }
276
277         // sets the time stamp for the transaction
278
Date JavaDoc myDate = new Date JavaDoc();
279         tx.setTxDate(myDate.toString());
280
281         // enlist all connections using the event callback this should be done in UserTransaction
282
// was done in doAttach
283
Stack JavaDoc curStack = (Stack JavaDoc) eventListStack.get();
284
285         if (curStack != null) { // if there is opened connections
286

287             try {
288                 List JavaDoc list = (List JavaDoc) curStack.peek();
289
290                 if (list != null) {
291
292                     for (Iterator JavaDoc it = list.iterator(); it.hasNext();) {
293                         ((ResourceManagerEvent) it.next()).enlistConnection(tx);
294                     }
295                 } else {
296
297                     if (TraceTm.jta.isDebugEnabled()) {
298                         TraceTm.jta.debug("Current.begin called with null list");
299                     }
300                 }
301             } catch (EmptyStackException JavaDoc e) {
302                 if (TraceTm.jta.isDebugEnabled()) {
303                     TraceTm.jta.debug("Current.begin called with empty stack");
304                 }
305             }
306         }
307     }
308
309     // ------------------------------------------------------------------
310
// Inflow Transaction implementation
311
// ------------------------------------------------------------------
312

313     /**
314      * Creates a new inflow transaction and associates it with the current thread.
315      *
316      * @param passxid <code>Xid</code> of the inflow transaction.
317      *
318      * @exception NotSupportedException Thrown if the thread is already
319      * associated with a transaction. (nested transaction are not
320      * supported)
321      *
322      * @exception SystemException Thrown if the transaction manager
323      * encounters an unexpected error condition
324      */

325
326     public void begin(javax.transaction.xa.Xid JavaDoc passxid) throws NotSupportedException JavaDoc, SystemException JavaDoc {
327         begin(passxid, (long) transactionTimeout);
328     }
329
330     /**
331      * Creates a new inflow transaction and associates it with the current thread.
332      *
333      * @param passxid <code>Xid</code> of the inflow transaction.
334      *
335      * @param timeout value of the timeout (in seconds). If the value is less than
336      * or equal to zero, the value will be set to the default value.
337      *
338      * @exception NotSupportedException Thrown if the thread is already
339      * associated with a transaction. (nested transaction are not
340      * supported)
341      *
342      * @exception SystemException Thrown if the transaction manager
343      * encounters an unexpected error condition
344      */

345
346     public void begin(javax.transaction.xa.Xid JavaDoc passxid, long timeout) throws NotSupportedException JavaDoc, SystemException JavaDoc {
347
348         if (TraceTm.jta.isDebugEnabled()) {
349             TraceTm.jta.debug("begin inflow transaction, timeout = " + timeout);
350         }
351
352         if (timeout <= 0) {
353             timeout = defaultTimeout;
354         }
355         
356         // checks that no transaction is already associated with this thread.
357
TransactionImpl tx = (TransactionImpl) threadTx.get();
358
359         if (TraceTm.jta.isDebugEnabled()) {
360             TraceTm.jta.debug("threadTx.get= " + threadTx.toString());
361         }
362
363         if (tx != null) {
364             if (txXids.containsValue(tx)) {
365                 throw new NotSupportedException JavaDoc("Nested transactions not supported");
366             } else {
367                 if (TraceTm.jta.isDebugEnabled()) {
368                     TraceTm.jta.debug("Resetting current tx = " + tx + " since it is already completed.");
369                 }
370             }
371         }
372
373         // stores the passed xid components
374
XidImpl pxid = new XidImpl(passxid);
375
376         // creates a new TransactionImpl object
377
// - May raise SystemException
378
tx = new TransactionImpl(pxid, (int) timeout);
379
380         if (TraceTm.jta.isDebugEnabled()) {
381             TraceTm.jta.debug("tx=" + tx);
382         }
383
384         // associates transaction with current thread
385
threadTx.set(tx);
386
387         if (TraceTm.jta.isDebugEnabled()) {
388             TraceTm.jta.debug("threadTx.set= " + threadTx.toString());
389         }
390
391         // associates this Tx with the Xid
392
putTxXid(pxid, tx);
393
394         // sets a timer for the transaction
395
if (timermgr != null) {
396             tx.setTimer(timermgr.addTimer(tx, (int) timeout, null, false));
397         }
398
399         // sets the time stamp for the transaction
400
Date JavaDoc myDate = new Date JavaDoc();
401         tx.setTxDate(myDate.toString());
402     }
403
404     /**
405      * Gets the inflow transaction object that represents the transaction context of
406      * the calling thread.
407      *
408      * @return the XATerminator object representing the inflow transaction
409      * associated with the calling thread. If the calling thread is
410      * not associated with an inflow transaction, a null object reference
411      * is returned.
412      *
413      * @exception XAException Thrown if the transaction manager
414      * encounters an unexpected error condition
415      */

416
417     public XATerminator JavaDoc getXATerminator() throws XAException JavaDoc {
418
419         XATerminator JavaDoc xaterm = null;
420
421         try {
422             xaterm = new XATerminatorImpl();
423         } catch (XAException JavaDoc e) {
424             if (TraceTm.jta.isDebugEnabled()) {
425                 TraceTm.jta.debug("Cannot create XATerminatorImpl"+ e);
426             }
427         }
428
429         return xaterm;
430     }
431
432     // ------------------------------------------------------------------
433
// End of Inflow Transaction implementation
434
// ------------------------------------------------------------------
435

436
437     /**
438       * Commits the transaction associated with the current thread. When this
439       * method completes, the thread becomes associated with no transaction.
440       *
441       * @exception RollbackException Thrown to indicate that the transaction
442       * has been rolled back rather than committed.
443       *
444       * @exception HeuristicMixedException Thrown to indicate that a heuristic
445       * decision was made and that some relevant updates have been committed
446       * while others have been rolled back.
447       *
448       * @exception HeuristicRollbackException Thrown to indicate that a
449       * heuristic decision was made and that some relevant updates have been
450       * rolled back.
451       *
452       * @exception SecurityException Thrown to indicate that the thread is not
453       * allowed to commit the transaction.
454       *
455       * @exception IllegalStateException Thrown if the current thread is not
456       * associated with a transaction.
457       *
458       * @exception SystemException Thrown if the transaction manager encounters
459       * an unexpected error condition
460       */

461
462     public void commit()
463         throws
464             RollbackException JavaDoc,
465             HeuristicMixedException JavaDoc,
466             HeuristicRollbackException JavaDoc,
467             SecurityException JavaDoc,
468             IllegalStateException JavaDoc,
469             SystemException JavaDoc {
470
471         if (TraceTm.jta.isDebugEnabled()) {
472             TraceTm.jta.debug("commit transaction ");
473         }
474
475         // Get Transaction
476
TransactionImpl tx = (TransactionImpl) getTransaction();
477
478         if (tx == null) {
479             throw new IllegalStateException JavaDoc("Cannot get Transaction for commit");
480         }
481
482         if (TraceTm.jta.isDebugEnabled()) {
483             TraceTm.jta.debug("tx=" + tx);
484         }
485         
486         // Commit Transaction. Exceptions may be raised!
487
try {
488             tx.commit();
489         } finally {
490             // Dissociates the transaction from current thread
491
// Has not been done in doDetach because we need to
492
// be in the transactional context for beforeCompletion
493
threadTx.set(null);
494             
495             // Reset the timeout to the default timeout
496
transactionTimeout = defaultTimeout;
497             
498             if (TraceTm.jta.isDebugEnabled()) {
499                 TraceTm.jta.debug("threadTx.set= null");
500                 TraceTm.jta.debug("reset timeout= " + defaultTimeout);
501             }
502         }
503     }
504
505     /**
506      * Rolls back the transaction associated with the current thread. When this
507      * method completes, the thread becomes associated with no transaction.
508      *
509      * @exception SecurityException Thrown to indicate that the thread is not
510      * allowed to roll back the transaction.
511      *
512      * @exception IllegalStateException Thrown if the current thread is not
513      * associated with a transaction.
514      *
515      * @exception SystemException Thrown if the transaction manager
516      * encounters an unexpected error condition
517      */

518     public void rollback() throws IllegalStateException JavaDoc, SecurityException JavaDoc, SystemException JavaDoc {
519
520         if (TraceTm.jta.isDebugEnabled()) {
521             TraceTm.jta.debug("Current.rollback()");
522         }
523
524         // Get Transaction
525
TransactionImpl tx = (TransactionImpl) getTransaction();
526
527         if (tx == null) {
528             throw new IllegalStateException JavaDoc("Cannot get Transaction for rollback");
529         }
530         
531         threadTx.set(null);
532
533         if (TraceTm.jta.isDebugEnabled()) {
534             TraceTm.jta.debug("threadTx.set= null");
535         }
536
537         // Roll back the transaction. Exceptions may be raised!
538
tx.rollback();
539         
540         if (TraceTm.jta.isDebugEnabled()) {
541             TraceTm.jta.debug("reset timeout= " + defaultTimeout);
542         }
543         
544         transactionTimeout = defaultTimeout;
545         
546     }
547
548     /**
549      * Modify the transaction associated with the current thread such that the
550      * only possible outcome of the transaction is to roll back the transaction.
551      *
552      * @exception IllegalStateException Thrown if the current thread is not
553      * associated with a transaction.
554      *
555      * @exception SystemException Thrown if the transaction manager
556      * encounters an unexpected error condition
557      */

558
559     public void setRollbackOnly() throws IllegalStateException JavaDoc, SystemException JavaDoc {
560
561         if (TraceTm.jta.isDebugEnabled()) {
562             TraceTm.jta.debug("Current.setRollbackOnly()");
563         }
564         // Get Transaction
565
TransactionImpl tx = (TransactionImpl) getTransaction();
566
567         if (tx == null) {
568             throw new IllegalStateException JavaDoc("Cannot get Transaction for setRollbackOnly");
569         }
570
571         // Set transaction rollback only. Exceptions may be raised!
572
tx.setRollbackOnly();
573     }
574
575     /**
576       * Returns the status of the transaction associated with the current
577       * thread.
578       *
579       * @return transaction status. If no transaction is associated with the
580       * current thread, this method returns the Status.NoTransaction value.
581       *
582       * @exception SystemException Thrown if the transaction manager
583       * encounters an unexpected error condition
584       */

585
586     public int getStatus() throws SystemException JavaDoc {
587
588         if (TraceTm.jta.isDebugEnabled()) {
589             TraceTm.jta.debug("Current.getStatus()");
590         }
591
592         // Get Transaction
593
TransactionImpl tx = (TransactionImpl) getTransaction();
594
595         if (tx == null) {
596             return Status.STATUS_NO_TRANSACTION;
597         }
598
599         // Get TX status. Exceptions may be raised!
600
return tx.getStatus();
601     }
602
603     /**
604       * Modifies the value of the timeout value that is associated with the
605       * transactions started by the current thread with the begin method.
606       *
607       * If an application has not called this method, the transaction
608       * service uses some default value for the transaction timeout.
609       *
610       * @param timeout value of the timeout (in seconds). If the value is zero,
611       * the transaction service restores the default value.
612       *
613       * @exception SystemException Thrown if the transaction manager
614       * encounters an unexpected error condition
615       */

616
617     public void setTransactionTimeout(int timeout) throws SystemException JavaDoc {
618         
619         if (TraceTm.jta.isDebugEnabled()) {
620             TraceTm.jta.debug("timeout= "+ timeout);
621         }
622         
623         // checks that no transaction is already associated with this thread.
624
// If one is, then we have a running transaction (ut.begin) and we must
625
// wait until transaction completes (ut.commit or ut.rollback)
626

627         TransactionImpl tx = (TransactionImpl) threadTx.get();
628
629         if (tx != null) {
630             if (txXids.containsValue(tx)) {
631                 if (TraceTm.jta.isDebugEnabled()) {
632                     TraceTm.jta.debug("Cannot reset transaction timeout, tx in execution");
633                 }
634                 // Cannot reset transaction timeout, tx (ut.begin)in execution, ignore.
635
return;
636             }
637         }
638         
639         if (timeout > 0) {
640             transactionTimeout = timeout;
641         } else {
642             transactionTimeout = defaultTimeout;
643         }
644         
645         if (TraceTm.jta.isDebugEnabled()) {
646             TraceTm.jta.debug("Resetting transaction timeout= " + transactionTimeout);
647         }
648     }
649     
650     /**
651      * Modifies the value of the recovery value that is associated with the
652      * transactions started by the current thread with the begin method.
653      *
654      * If an application has not called this method, the transaction
655      * service uses the default value of 'false' for recovery.
656      *
657      * @param recovery value of the recovery (true or faluse). If the value is
658      * false, recovery of transactions is disabled.
659      *
660      * @exception SystemException Thrown if the transaction manager
661      * encounters an unexpected error condition
662      */

663
664    public void setTransactionRecovery(boolean recovery) throws SystemException JavaDoc {
665
666        if (TraceTm.recovery.isDebugEnabled()) {
667            TraceTm.recovery.debug("recovery="+ recovery);
668        }
669
670        if (recovery) {
671            transactionRecovery = recovery;
672        } else {
673            transactionRecovery = DEFAULT_RECOVERY;
674        }
675    }
676
677     // ------------------------------------------------------------------
678
// TransactionManager implementation
679
// (only the methods that are not already in UserTransaction)
680
// ------------------------------------------------------------------
681

682     /**
683      * Gets the transaction object that represents the transaction context of
684      * the calling thread.
685      *
686      * @return the Transaction object representing the transaction
687      * associated with the calling thread. If the calling thread is
688      * not associated with a transaction, a null object reference
689      * is returned.
690      *
691      * @exception SystemException Thrown if the transaction manager
692      * encounters an unexpected error condition
693      */

694
695     public Transaction JavaDoc getTransaction() throws SystemException JavaDoc {
696         Transaction JavaDoc ret = (Transaction JavaDoc) threadTx.get();
697
698         if (TraceTm.jta.isDebugEnabled()) {
699             TraceTm.jta.debug("threadTx.get= " + threadTx.toString());
700             TraceTm.jta.debug("Transaction ret= " + ret);
701         }
702
703         return ret;
704     }
705
706     /**
707      * Resumes the transaction context association of the calling thread with
708      * the transaction represented by the supplied Transaction object. When this
709      * method returns, the calling thread is associated with the transaction
710      * context specified.
711      * <p><em>Warning</em>: No XA start is done here. We suppose it is already
712      * done after a <code>getConnection()</code>.
713      * </p>
714      * The supposed programming model is: <ol>
715      * <li><code>getConnection()</code></li>
716      * <li>SQL code</li>
717      * <li><code>connection.close()</code</li>
718      * </ol>
719      *
720      * @param tobj The <code>Transaction</code> object that represents the
721      * transaction to be resumed.
722      *
723      * @exception InvalidTransactionException Thrown if the parameter
724      * transaction object contains an invalid transaction
725      *
726      * @exception IllegalStateException Thrown if the thread is already
727      * associated with another transaction.
728      *
729      * @exception SystemException Thrown if the transaction manager
730      * encounters an unexpected error condition
731      */

732
733     public void resume(Transaction JavaDoc tobj) throws InvalidTransactionException JavaDoc, IllegalStateException JavaDoc, SystemException JavaDoc {
734
735         if (TraceTm.jta.isDebugEnabled()) {
736             TraceTm.jta.debug("resume transaction");
737         }
738
739         // invalid Transaction
740
if (tobj == null) {
741             TraceTm.jotm.error("resume: null arg.");
742             throw new InvalidTransactionException JavaDoc("resume(null) is not valid");
743         }
744
745         if (TraceTm.jta.isDebugEnabled()) {
746             TraceTm.jta.debug("tx="+ tobj);
747         }
748
749         // Checks that the thread is not already associated to ANOTHER transaction
750
Transaction JavaDoc mytx = (Transaction JavaDoc) threadTx.get();
751
752         if (TraceTm.jta.isDebugEnabled()) {
753             TraceTm.jta.debug("threadTx.get= " + threadTx.toString());
754         }
755
756         if (mytx != null) {
757             if (mytx.equals(tobj)) {
758                 if (TraceTm.jta.isDebugEnabled()) {
759                     TraceTm.jta.debug("nothing to do");
760                 }
761                 return;
762             }
763             TraceTm.jotm.error("resume: already associated with another transaction.");
764             throw new IllegalStateException JavaDoc("the thread is already associated with another transaction.");
765         }
766
767         // test for type before cast
768
if (!(tobj instanceof TransactionImpl)) {
769             TraceTm.jotm.error("resume: non TransactionImpl arg.");
770             throw new InvalidTransactionException JavaDoc("resume(" + tobj.getClass().getName() + ") is not valid");
771         }
772
773         // Associates this Tx with the current thread
774
threadTx.set(tobj);
775
776         if (TraceTm.jta.isDebugEnabled()) {
777             TraceTm.jta.debug("threadTx.set= " + threadTx.toString());
778         }
779
780         // attach suspended resources
781
try {
782             ((TransactionImpl) tobj).doAttach(XAResource.TMRESUME);
783         } catch (RollbackException JavaDoc e) {
784             // never.
785
TraceTm.jotm.error("RollbackException occured in resume()");
786             throw new SystemException JavaDoc("RollbackException in occured in resume() " + e.getMessage());
787         }
788     }
789
790     /**
791       * Suspends the transaction currently associated with the calling thread
792       * and return a <code>Transaction</code> object that represents the
793       * transaction context being suspended.
794       * If the calling thread is not
795       * associated with a transaction, the method returns
796       * <code>null</code>. When this method returns, the calling thread is
797       * associated with no transaction.
798       <p><em>Warning</em>: No XA start is done here. We suppose it is already
799       done after a <code>getConnection()</code>.
800      * </p>
801      * The supposed programming model is: <ol>
802      * <li><code>getConnection()</code></li>
803      * <li>SQL code</li>
804      * <li><code>connection.close()</code</li>
805      * </ol>
806      *
807      * @return Transaction object representing the suspended transaction.
808      *
809      * @exception SystemException Thrown if the transaction manager
810      * encounters an unexpected error condition
811      *
812      * @exception SystemException Thrown if the transaction manager
813      * encounters an unexpected error condition
814      */

815
816     public Transaction JavaDoc suspend() throws SystemException JavaDoc {
817
818         if (TraceTm.jta.isDebugEnabled()) {
819             TraceTm.jta.debug("suspend transaction");
820         }
821
822         /// TMSUSPEND should be defined in Transaction.
823
TransactionImpl tx = (TransactionImpl) threadTx.get();
824
825         if (TraceTm.jta.isDebugEnabled()) {
826             TraceTm.jta.debug("threadTx.get= " + threadTx.toString());
827         }
828
829         if (tx != null) {
830
831             if (TraceTm.jta.isDebugEnabled()) {
832                 TraceTm.jta.debug("tx="+ tx);
833             }
834             
835             tx.doDetach(XAResource.TMSUSPEND);
836             threadTx.set(null);
837
838             if (TraceTm.jta.isDebugEnabled()) {
839                 TraceTm.jta.debug("threadTx.set= null");
840             }
841         }
842
843         return tx;
844     }
845
846     // ------------------------------------------------------------------
847
// ResourceManagerEventListener implementation
848
// Should be in UserTransaction only.
849
// ------------------------------------------------------------------
850

851     /**
852       * @see org.objectweb.transaction.jta.ResourceManagerEventListener#connectionOpened(org.objectweb.transaction.jta.ResourceManagerEvent)
853       */

854
855     public void connectionOpened(ResourceManagerEvent event) {
856
857         if (TraceTm.jta.isDebugEnabled()) {
858             TraceTm.jta.debug("Current.connectionOpened " + this);
859         }
860
861         List JavaDoc list = null;
862         Stack JavaDoc curStack = (Stack JavaDoc) eventListStack.get();
863         // get thread local stack
864

865         if (curStack == null) {
866             // no stack yet : create one
867
eventListStack.set(curStack = new Stack JavaDoc());
868         } else { // pop list from current stack
869
try {
870                 list = (List JavaDoc) curStack.pop();
871             } catch (EmptyStackException JavaDoc e) {
872                 // ignore: might happen if thread used by session bean
873
}
874         }
875
876         // no list yet: create one
877
if (list == null)
878             list = new Vector JavaDoc(1);
879
880         // add event to list
881
list.add(event);
882
883