KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > tm > TransactionImpl


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.tm;
23
24 import java.lang.reflect.Proxy JavaDoc;
25 import java.rmi.NoSuchObjectException JavaDoc;
26 import java.rmi.RemoteException JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collections JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.HashSet JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Set JavaDoc;
35
36 import javax.resource.spi.work.Work JavaDoc;
37 import javax.resource.spi.work.WorkCompletedException JavaDoc;
38 import javax.resource.spi.work.WorkException JavaDoc;
39 import javax.transaction.HeuristicCommitException JavaDoc;
40 import javax.transaction.HeuristicMixedException JavaDoc;
41 import javax.transaction.HeuristicRollbackException JavaDoc;
42 import javax.transaction.RollbackException JavaDoc;
43 import javax.transaction.Status JavaDoc;
44 import javax.transaction.Synchronization JavaDoc;
45 import javax.transaction.SystemException JavaDoc;
46 import javax.transaction.Transaction JavaDoc;
47 import javax.transaction.TransactionRolledbackException JavaDoc;
48 import javax.transaction.xa.XAException JavaDoc;
49 import javax.transaction.xa.XAResource JavaDoc;
50 import javax.transaction.xa.Xid JavaDoc;
51
52 import org.jboss.logging.Logger;
53 import org.jboss.tm.integrity.TransactionIntegrity;
54 import org.jboss.tm.recovery.HeuristicStatus;
55 import org.jboss.tm.recovery.LogRecord;
56 import org.jboss.tm.recovery.RecoveryLogger;
57 import org.jboss.tm.recovery.RecoveryTestingException;
58 import org.jboss.tm.recovery.TxCompletionHandler;
59 import org.jboss.tm.recovery.XAResourceAccess;
60 import org.jboss.tm.recovery.XAWork;
61 import org.jboss.tm.remoting.interfaces.Coordinator;
62 import org.jboss.tm.remoting.interfaces.HeuristicHazardException;
63 import org.jboss.tm.remoting.interfaces.RecoveryCoordinator;
64 import org.jboss.tm.remoting.interfaces.Resource;
65 import org.jboss.tm.remoting.interfaces.TransactionAlreadyPreparedException;
66 import org.jboss.tm.remoting.interfaces.TransactionInactiveException;
67 import org.jboss.tm.remoting.interfaces.TransactionNotPreparedException;
68 import org.jboss.tm.remoting.interfaces.TxPropagationContext;
69 import org.jboss.tm.remoting.interfaces.Vote;
70 import org.jboss.util.timeout.Timeout;
71 import org.jboss.util.timeout.TimeoutFactory;
72 import org.jboss.util.timeout.TimeoutTarget;
73
74 /**
75  * Our <code>Transaction</code> implementation.
76  *
77  * @author <a HREF="mailto:rickard.oberg@telkel.com">Rickard Oberg</a>
78  * @author <a HREF="mailto:marc.fleury@telkel.com">Marc Fleury</a>
79  * @author <a HREF="mailto:osh@sparre.dk">Ole Husgaard</a>
80  * @author <a HREF="mailto:toby.allsopp@peace.com">Toby Allsopp</a>
81  * @author <a HREF="mailto:jason@planet57.com">Jason Dillon</a>
82  * @author <a HREF="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
83  * @author <a HREF="mailto:bill@jboss.org">Bill Burke</a>
84  * @author <a HREF="mailto:adrian@jboss.com">Adrian Brock</a>
85  * @author <a HREF="mailto:reverbel@ime.usp.br">Francisco Reverbel</a>
86  * @author <a HREF="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
87  * @version $Revision: 43490 $
88  * @see TxManager
89  */

90 public class TransactionImpl
91         implements Transaction JavaDoc, TimeoutTarget
92 {
93    // Constants -----------------------------------------------------
94

95    /**
96     * Code meaning "no heuristics seen",
97     * must not be XAException.XA_HEURxxx
98     */

99    private static final int HEUR_NONE = 0;
100
101    /**
102     * Code meaning "heuristics seen, but no additional info is available",
103     * must not be XAException.XA_HEURxxx
104     */

105    private static final int HEUR_UNKNOWN = XAException.XA_RETRY;
106
107    // Resource states
108
private final static int RS_NEW = 0; // not yet enlisted
109
private final static int RS_ENLISTED = 1; // enlisted
110
private final static int RS_SUSPENDED = 2; // suspended
111
private final static int RS_ENDED = 3; // not associated
112
private final static int RS_VOTE_READONLY = 4; // voted read-only
113
private final static int RS_VOTE_OK = 5; // voted ok
114
private final static int RS_FORGOT = 6; // RM has forgotten
115
private final static int RS_COMMITTED = 7; // committed
116
private final static int RS_ROLLEDBACK = 8; // rolledback
117
private final static int RS_HEUR_OUTCOME = 8; // had an heuristic outcome
118
private final static int RS_ERROR = 9; // error condition (no use retrying)
119

120    // Attributes ----------------------------------------------------
121

122    /**
123     * True if trace messages should be logged.
124     */

125    private boolean trace = log.isTraceEnabled();
126
127    /**
128     * The ID of this transaction.
129     */

130    private XidImpl xid;
131
132    private HashSet JavaDoc threads = new HashSet JavaDoc(1);
133
134    private Map JavaDoc transactionLocalMap = Collections.synchronizedMap(new HashMap JavaDoc());
135
136    private Throwable JavaDoc cause;
137
138    /**
139     * True for a foreign transaction that has been imported either through an
140     * DTM/OTS transaction propagation context or via JCA transaction inflow;
141     * false for a locally started transaction.
142     */

143    private final boolean foreignTx;
144
145    /**
146     * This transaction's parent coordinator, or null if this transaction does
147     * not have a parent coordinator. A transaction with no parent coodinator
148     * is either a (locally started) root transaction or a foreign transaction
149     * that has been imported via JCA transaction inflow.
150     */

151    private Coordinator parentCoordinator = null;
152
153    /**
154     * The resource registered by transaction with the parent coordinator.
155     */

156    private Resource JavaDoc registeredResource = null;
157    
158    /**
159     * This transaction's recovery coordinator, or null this transaction did not
160     * register itself as a remote resource with the parent coordinator.
161     */

162    private RecoveryCoordinator recoveryCoordinator = null;
163    
164    /**
165     * The inbound branch qualifier, if this is a foreign transaction that has
166     * been imported via JCA transaction inflow, or null otherwise.
167     */

168    private byte[] inboundBranchQualifier = null;
169
170    /**
171     * The synchronizations to call back.
172     */

173    private Synchronization JavaDoc[] sync = new Synchronization JavaDoc[3];
174
175    /**
176     * Size of allocated synchronization array.
177     */

178    private int syncAllocSize = 3;
179
180    /**
181     * Count of synchronizations for this transaction.
182     */

183    private int syncCount = 0;
184
185    /**
186     * A list of the XAResources that have participated in this transaction.
187     */

188    private ArrayList JavaDoc xaResources = new ArrayList JavaDoc(3);
189
190    /**
191     * A list of the remote resources that have participated in this transaction.
192     */

193    private ArrayList JavaDoc remoteResources = new ArrayList JavaDoc(3);
194
195    /**
196     * The XAResource used in the last resource gambit
197     */

198    private EnlistedXAResource lastResource;
199
200    /**
201     * Flags that it is too late to enlist new resources.
202     */

203    private boolean resourcesEnded = false;
204
205    /**
206     * Last branch id used.
207     */

208    private long lastBranchId = 0;
209
210    /**
211     * Status of this transaction.
212     */

213    private int status;
214
215    /**
216     * The heuristics status of this transaction.
217     */

218    private int heuristicCode = HEUR_NONE;
219
220    /**
221     * The time when this transaction was started.
222     */

223    private long start;
224
225    /**
226     * The timeout handle for this transaction.
227     */

228    private Timeout timeout;
229
230    /**
231     * Timeout in millisecs
232     */

233    private long timeoutPeriod;
234
235    /**
236     * Mutex for thread-safety. This should only be changed in the
237     * <code>lock()</code> and <code>unlock()</code> methods.
238     */

239    private Thread JavaDoc locked = null;
240    
241    /**
242     * The lock depth
243     */

244    private int lockDepth = 0;
245
246    /**
247     * Any current work associated with the transaction
248     */

249    private Work JavaDoc work;
250
251    /**
252     * Flags that we are done with this transaction and that it can be reused.
253     */

254    private boolean done = false;
255
256    /**
257     * This transaction's DTM propagation context.
258     */

259    private TxPropagationContext dtmPropagationContext = null;
260
261    /**
262     * This transaction's OTS propagation context.
263     */

264    private Object JavaDoc otsPropagationContext = null;
265
266    /**
267     * This transaction's TxCompletionHandler.
268     */

269    private TxCompletionHandler completionHandler = null;
270    
271    /**
272     * Number of XA resources that reported transient problems.
273     */

274    private int xaResourcesToRetry = 0;
275    
276    /**
277     * Number of remote resources that reported transient problems.
278     */

279    private int remoteResourcesToRetry = 0;
280    
281    /**
282     * Timeout before calling <code>replayCompletion</code> on the recovery
283     * the coordinator when waiting for the coordinator in the prepared state.
284     */

285    private Timeout preparedTimeout = null;
286    
287    /**
288     * Timeout before retrying commit or rollback calls on XA resources that
289     * reported transient problems.
290     */

291    private Timeout xaRetryTimeout = null;
292    
293    /**
294     * List of <code>EnlistedXAResource</code> instances with heuristic
295     * decisions.
296     */

297    private List JavaDoc xaResourcesWithHeuristicDecisions = null;
298    
299    /**
300     * List of <code>EnlistedRemoteResource</code> instances with heuristic
301     * decisions.
302     */

303    private List JavaDoc remoteResourcesWithHeuristicDecisions = null;
304    
305    /**
306     * Counts how many resources were committed.
307     */

308    private int committedResources = 0;
309    
310    /**
311     * Counts how many resources were rolled back.
312     */

313    private int rolledbackResources = 0;
314    
315    /**
316     * True if this <code>TransactionImpl</code> could not reach a resource
317     * during the second phase of 2PC.
318     */

319    private boolean heuristicHazard = false;
320       
321    // Static --------------------------------------------------------
322

323    /**
324     * Class logger, we don't want a new logger with every transaction.
325     */

326    private static Logger log = Logger.getLogger(TransactionImpl.class);
327
328    /**
329     * Factory for Xid instances of specified class.
330     * This is set from the <code>TransactionManagerService</code>
331     * MBean.
332     */

333    static XidFactoryBase xidFactory;
334
335    static XAExceptionFormatter xaExceptionFormatter;
336
337    /** The timeout factory */
338    static TimeoutFactory timeoutFactory = TimeoutFactory.getSingleton();
339    
340    /**
341     * CoordinatorFactory that creates remote references to DTM coordinators,
342     * or null if the DTM is not employed.
343     */

344    private static CoordinatorFactory dtmCoordinatorFactory = null;
345
346    /**
347     * ResourceFactory that creates remote references to DTM resources,
348     * or null if the DTM is not employed.
349     */

350    private static ResourceFactory dtmResourceFactory = null;
351
352    /**
353     * ResourceFactory that creates remote references to OTS resources,
354     * or null if OTS is not employed.
355     */

356    private static ResourceFactory otsResourceFactory = null;
357
358    /**
359     * Factory that creates OTS transaction propagation contexts,
360     * or null if OTS is not employed.
361     */

362    private static OTSContextFactory otsContextFactory = null;
363
364    /**
365     * True if coordinator interposition is enabled.
366     */

367    private static boolean interpositionEnabled = false;
368
369    /**
370     * Object that converts between strings and remote references for
371     * DTM objects, or null if DTM is not employed.
372     */

373    private static StringRemoteRefConverter dtmStrRemoteRefConverter = null;
374    
375    /**
376     * Object that converts between strings and remote references for
377     * OTS objects, or null if OTS is not employed.
378     */

379    private static StringRemoteRefConverter otsStrRemoteRefConverter = null;
380    
381    /**
382     * This static code is only present for testing purposes so a
383     * tm can be usable without a lot of setup.
384     */

385    public static XidFactoryBase defaultXidFactory()
386    {
387       if (xidFactory == null)
388       {
389          XidFactoryImpl impl = new XidFactoryImpl();
390          impl.start();
391          xidFactory = impl;
392       }
393       return xidFactory;
394    }
395
396    /**
397     * Setter for <code>dtmCoordinatorFactory</code>.
398     */

399    static void setDTMCoordinatorFactory(CoordinatorFactory dtmCoordinatorFactory)
400    {
401       TransactionImpl.dtmCoordinatorFactory = dtmCoordinatorFactory;
402    }
403
404    /**
405     * Setter for <code>dtmResourceFactory</code>.
406     */

407    static void setDTMResourceFactory(ResourceFactory dtmResourceFactory)
408    {
409       TransactionImpl.dtmResourceFactory = dtmResourceFactory;
410    }
411
412    /**
413     * Setter for <code>otsResourceFactory</code>.
414     */

415    static void setOTSResourceFactory(ResourceFactory otsResourceFactory)
416    {
417       TransactionImpl.otsResourceFactory = otsResourceFactory;
418    }
419
420    /**
421     * Setter for <code>otsContextFactory</code>.
422     */

423    static void setOTSContextFactory(OTSContextFactory otsContextFactory)
424    {
425       TransactionImpl.otsContextFactory = otsContextFactory;
426    }
427
428    /**
429     * Setter for <code>interpositionEnabled</code>.
430     */

431    static void setInterpositionEnabled(boolean interpositionEnabled)
432    {
433       TransactionImpl.interpositionEnabled = interpositionEnabled;
434    }
435
436    /**
437     * Getter for <code>interpositionEnabled</code>.
438     */

439    static boolean getInterpositionEnabled()
440    {
441       return TransactionImpl.interpositionEnabled;
442    }
443
444    /**
445     * Setter for <code>dtmStrRemoteRefConverter</code>.
446     */

447    static void setDTMStrRemoteRefConverter(
448          StringRemoteRefConverter dtmStrRemoteRefConverter)
449    {
450       TransactionImpl.dtmStrRemoteRefConverter = dtmStrRemoteRefConverter;
451    }
452
453    /**
454     * Setter for <code>otsStrRemoteRefConverter</code>.
455     */

456    static void setOTSStrRemoteRefConverter(
457          StringRemoteRefConverter otsStrRemoteRefConverter)
458    {
459       TransactionImpl.otsStrRemoteRefConverter = otsStrRemoteRefConverter;
460    }
461
462    /**
463     * Converts a stringfied reference to a remote resource back to a remote
464     * reference.
465     *
466     * @param strResource a stringfied reference to a remote resource
467     * @return a remote reference to the resource.
468     */

469    static Resource JavaDoc stringToResource(String JavaDoc strResource)
470    {
471       if (strResource.startsWith("IOR:"))
472       {
473          if (otsStrRemoteRefConverter != null)
474             return otsStrRemoteRefConverter.stringToResource(strResource);
475          else
476             throw new IllegalArgumentException JavaDoc();
477       }
478       else
479       {
480          if (dtmStrRemoteRefConverter != null)
481             return dtmStrRemoteRefConverter.stringToResource(strResource);
482          else
483             throw new IllegalArgumentException JavaDoc();
484       }
485    }
486
487    /**
488     * Converts a stringfied reference to a remote recovery coordinator back
489     * to a remote reference.
490     *
491     * @param strRecCoordinator a stringfied reference to a remote recovery
492     * coordinator
493     * @return a remote reference to the recovery coordinator.
494     */

495    static RecoveryCoordinator stringToRecoveryCoordinator(
496                                                    String JavaDoc strRecCoordinator)
497    {
498       if (strRecCoordinator.startsWith("IOR:"))
499       {
500          if (otsStrRemoteRefConverter != null)
501             return otsStrRemoteRefConverter.stringToRecoveryCoordinator(
502                                                             strRecCoordinator);
503          else
504             throw new IllegalArgumentException JavaDoc();
505       }
506       else
507       {
508          if (dtmStrRemoteRefConverter != null)
509             return dtmStrRemoteRefConverter.stringToRecoveryCoordinator(
510                                                             strRecCoordinator);
511          else
512             throw new IllegalArgumentException JavaDoc();
513       }
514    }
515
516    /**
517     * Takes a remote reference to a resource and converts it to a string.
518     *
519     * @param res a remote reference to a resource
520     * @return a string that represents the remote resource.
521     */

522    static String JavaDoc resourceToString(Resource JavaDoc res)
523    {
524       if (Proxy.isProxyClass(res.getClass()))
525       {
526          if (dtmStrRemoteRefConverter != null)
527             return dtmStrRemoteRefConverter.resourceToString(res);
528          else
529             throw new IllegalArgumentException JavaDoc();
530       }
531       else
532       {
533          if (otsStrRemoteRefConverter != null)
534             return otsStrRemoteRefConverter.resourceToString(res);
535          else
536             throw new IllegalArgumentException JavaDoc();
537       }
538    }
539
540    /**
541     * Takes a remote reference to a recovery coordinator and converts it to a
542     * string.
543     *
544     * @param recoveryCoord a remote reference to a recovery coordinator
545     * @return a string that represents the remote recovery coordinator.
546     */

547    static String JavaDoc recoveryCoordinatorToString(RecoveryCoordinator recoveryCoord)
548    {
549       if (Proxy.isProxyClass(recoveryCoord.getClass()))
550       {
551          if (dtmStrRemoteRefConverter != null)
552             return dtmStrRemoteRefConverter.recoveryCoordinatorToString(
553                                                                recoveryCoord);
554          else
555             throw new IllegalArgumentException JavaDoc();
556       }
557       else
558       {
559          if (otsStrRemoteRefConverter != null)
560             return otsStrRemoteRefConverter.recoveryCoordinatorToString(
561                                                                recoveryCoord);
562          else
563             throw new IllegalArgumentException JavaDoc();
564       }
565    }
566    
567    // Constructors --------------------------------------------------
568

569    /**
570     * Constructor for transactions started locally.
571     */

572    TransactionImpl(long timeout)
573    {
574       foreignTx = false;
575       xid = xidFactory.newXid();
576
577       status = Status.STATUS_ACTIVE;
578
579       start = System.currentTimeMillis();
580       this.timeout = timeoutFactory.createTimeout(start + timeout, this);
581       this.timeoutPeriod = timeout;
582       if (trace)
583          log.trace("Created new instance for tx=" + toString());
584    }
585
586    /**
587     * Constructor for foreign transactions imported through DTM/OTS transaction
588     * propagation contexts.
589     */

590    TransactionImpl(GlobalId gid, Coordinator parentCoordinator, long timeout)
591    {
592       foreignTx = true;
593       xid = xidFactory.newBranch(gid);
594
595       this.parentCoordinator = parentCoordinator;
596
597       status = Status.STATUS_ACTIVE;
598
599       start = System.currentTimeMillis();
600       this.timeout = timeoutFactory.createTimeout(start + timeout, this);
601       this.timeoutPeriod = timeout;
602       if (trace)
603          log.trace("Created new instance for tx=" + toString());
604    }
605
606    /**
607     * Constructor for foreign transactions imported through the JCA transaction
608     * inflow mechanism.
609     */

610    TransactionImpl(GlobalId gid, byte[] inboundBranchQualifier, long timeout)
611    {
612       foreignTx = true;
613       xid = xidFactory.newBranch(gid);
614
615       this.inboundBranchQualifier = inboundBranchQualifier;
616
617       status = Status.STATUS_ACTIVE;
618
619       start = System.currentTimeMillis();
620       this.timeout = timeoutFactory.createTimeout(start + timeout, this);
621       this.timeoutPeriod = timeout;
622       if (trace)
623          log.trace("Created new instance for tx=" + toString());
624       
625    }
626    
627    /**
628     * Constructor to recreate a locally-started transaction that does not
629     * involve other transaction managers. It is intended to be called at
630     * recovery time, for recreating transactions that were in the committing
631     * state when the server crashed. Such a transaction completed the first
632     * phase of the 2PC protocol and logged the commit decision, but it must
633     * still send commit messages to some or all of its <code>XAResource</code>s.
634     *
635     * @param localId the local id of a locally-started transaction that is in
636     * the committing state and does not involve other transaction
637     * managers
638     * @param preparedXidBranches a list of <code>org.jboss.tm.XAWork</code>
639     * instances containing one element for each
640     * <code>XAResource</code> that is enlisted with the transaction
641     * and is still in the prepared state
642     * @param completionHandler the
643     * <code>org.jboss.tm.recovery.TxCompletionHandler</code> to be
644     * notifed when the second phase of the 2PC completes
645     * @param heurData either null or a <code>LogRecord.HeurData</code> instance
646     * contaning information on a locally-detected heuristic hazard.
647     */

648    TransactionImpl(long localId,
649                    List JavaDoc preparedXAWorkList,
650                    TxCompletionHandler completionHandler,
651                    LogRecord.HeurData heurData)
652    {
653       foreignTx = false;
654       xid = xidFactory.recreateXid(localId);
655       status = Status.STATUS_COMMITTING;
656       if (heurData != null)
657       {
658          heuristicCode = heurData.heuristicStatusCode;
659          heuristicHazard = heurData.locallyDetectedHeuristicHazard;
660       }
661       this.completionHandler = completionHandler;
662       for (Iterator JavaDoc it = preparedXAWorkList.iterator(); it.hasNext(); )
663       {
664          XAWork preparedXAWork = (XAWork) it.next();
665          EnlistedXAResource r = new EnlistedXAResource(preparedXAWork);
666          xaResources.add(r);
667
668       }
669       commitXAResourcesAfterTimeout();
670    }
671          
672    /**
673     * Constructor to recreate a locally-started transaction that involves other
674     * transaction managers. Involving other transaction managers means that
675     * there are remote <code>Resource</code>s enlisted with the transaction.
676     * This constructor is intended to be called at recovery time, for recreating
677     * transactions that were in the committing state when the server crashed.
678     * Such a transaction completed the first phase of the 2PC protocol and
679     * logged the commit decision, but it must still send commit messages to
680     * some or all of its resources (<code>XAResource</code>s and remote
681     * <code>Resource</code>s).
682     *
683     * @param localId the local id of a locally-started transaction that is in
684     * the committing state and involves other transaction managers
685     * @param preparedXAWorkList list of <code>org.jboss.tm.XAWork</code>
686     * instances containing one element for each
687     * <code>XAResource</code> that is enlisted with the transaction
688     * and is still in the prepared state
689     * @param resources an array with stringfied references for the remote
690     * <code>Resource</code>s enlisted with the transaction
691     * @param completionHandler the
692     * <code>org.jboss.tm.recovery.TxCompletionHandler</code> to be
693     * notifed when the second phase of the 2PC completes
694     * @param heurData either null or a <code>LogRecord.HeurData</code> instance
695     * contaning information on a locally-detected heuristic hazard.
696     */

697    TransactionImpl(long localId,
698                    List JavaDoc preparedXAWorkList,
699                    String JavaDoc[] resources,
700                    TxCompletionHandler completionHandler,
701                    LogRecord.HeurData heurData)
702    {
703       foreignTx = false;
704       xid = xidFactory.recreateXid(localId);
705       status = Status.STATUS_COMMITTING;
706       this.completionHandler = completionHandler;
707       if (heurData != null)
708       {
709          heuristicCode = heurData.heuristicStatusCode;
710          heuristicHazard = heurData.locallyDetectedHeuristicHazard;
711       }
712       for (Iterator JavaDoc it = preparedXAWorkList.iterator(); it.hasNext(); )
713       {
714          XAWork preparedXAWork = (XAWork) it.next();
715          EnlistedXAResource r = new EnlistedXAResource(preparedXAWork);
716          xaResources.add(r);
717       }
718       for (int i = 0; i < resources.length; i++)
719       {
720          EnlistedRemoteResource r =
721             new EnlistedRemoteResource(stringToResource(resources[i]), true);
722          remoteResources.add(r);
723       }
724       lock();
725       try
726       {
727          retryCommitRemoteResources();
728       }
729       finally
730       {
731          unlock();
732       }
733       if (preparedXAWorkList.size() > 0)
734       {
735          // Keep this TransactionImpl around to
736
// commit its XAResources at a later time.
737
commitXAResourcesAfterTimeout();
738       }
739       else if (remoteResourcesToRetry == 0 && heuristicCode == HEUR_NONE)
740       {
741          // This TransactionImpl is not needed anymore.
742
lock();
743          try
744          {
745             completeTransaction();
746          }
747          finally
748          {
749             unlock();
750          }
751       }
752       else
753       {
754          // Do nothing. Just keep this TransactionImpl around to receive
755
// replayCompletion calls from remote resources.
756
}
757    }
758
759    /**
760     * Constructor to recreate a foreign transaction that entered this virtual
761     * machine in a transaction context propagated along with a remote method
762     * invocation. This constructor is intended to be called at recovery time,
763     * for recreating transactions that were in the prepared state when the
764     * server crashed.
765     *
766     * @param localId the local id of a foreign transaction that entered this
767     * virtual machine in a transaction propagation context and is
768     * propagated along with a remote method invocation and is in
769     * the prepared state
770     * @param inboundFormatId format id part of the foreign transaction
771     * identifier that entered this virtual machine
772     * @param globalTransactionId global id part of the foreign transaction
773     * identifier that entered this virtual machine
774     * @param recoveryCoord an stringfied reference to the transaction branch's
775     * <code>RecovertyCoordinator</code>
776     * @param preparedXAWorkList a list of <code>org.jboss.tm.XAWork</code>
777     * instances containing one element for each
778     * <code>XAResource</code> enlisted with the transaction
779     * @param resources an array with stringfied references for the remote
780     * <code>Resource</code>s enlisted with the transaction
781     * @param completionHandler the
782     * <code>org.jboss.tm.recovery.TxCompletionHandler</code> to be
783     * notifed when the second phase of the 2PC completes
784     * @param heurData either null or a <code>LogRecord.HeurData</code> instance
785     * contaning information on a locally-detected heuristic hazard.
786     */

787    TransactionImpl(long localId,
788                    int inboundFormatId,
789                    byte[] globalTransactionId,
790                    String JavaDoc recoveryCoord,
791                    List JavaDoc preparedXAWorkList,
792                    String JavaDoc[] resources,
793                    TxCompletionHandler completionHandler,
794                    LogRecord.HeurData heurData)
795    {
796       foreignTx = true;
797       GlobalId globalId = new GlobalId(inboundFormatId, globalTransactionId);
798       xid = xidFactory.recreateXid(localId, globalId);
799       recoveryCoordinator = stringToRecoveryCoordinator(recoveryCoord);
800       if (heurData == null)
801          status = Status.STATUS_PREPARED;
802       else
803       {
804          status = heurData.transactionStatus;
805          heuristicCode = heurData.heuristicStatusCode;
806          heuristicHazard = heurData.locallyDetectedHeuristicHazard;
807       }
808       this.completionHandler = completionHandler;
809       for (Iterator JavaDoc it = preparedXAWorkList.iterator(); it.hasNext(); )
810       {
811          XAWork preparedXAWork = (XAWork) it.next();
812          EnlistedXAResource r = new EnlistedXAResource(preparedXAWork);
813          xaResources.add(r);
814       }
815       for (int i = 0; i < resources.length; i++)
816       {
817          EnlistedRemoteResource r =
818             new EnlistedRemoteResource(stringToResource(resources[i]), true);
819          remoteResources.add(r);
820       }
821       
822       // Set my registeredResource using the appropriate resource factory.
823
if (Proxy.isProxyClass(recoveryCoordinator.getClass()))
824       {
825          // DTM coordinator case
826
if (dtmResourceFactory != null)
827             registeredResource = dtmResourceFactory.createResource(localId);
828          else
829             log.warn("Error reconstructing TransactionImpl instance for tx="
830                      + toString() + " -- DTM resource factory missing." );
831       }
832       else
833       {
834          // OTS coordinator case
835
if (otsResourceFactory != null)
836             registeredResource = otsResourceFactory.createResource(localId);
837          else
838             log.warn("Error reconstructing TransactionImpl instance for tx=" +
839                      toString() + " -- OTS resource factory missing." );
840       }
841
842       if (status == Status.STATUS_PREPARED)
843       {
844          if (registeredResource != null)
845          {
846             createPreparedTimeout();
847             if (trace)
848                log.trace("Calling replayCompletion " +
849                          "on the recovery coordinator, tx=" + toString());
850             try
851             {
852                recoveryCoordinator.replayCompletion(registeredResource);
853             }
854             catch (NoSuchObjectException JavaDoc noCoordinator)
855             {
856                if (trace)
857                {
858                   log.trace("Exception in replayCompletion: no coordinator for tx="
859                             + toString(), noCoordinator);
860                   log.trace("Rolling back transaction branch, tx=" + toString());
861                }
862                try
863                {
864                   rollbackBranch();
865                }
866                catch (Exception JavaDoc e)
867                {
868                   if (trace)
869                      log.trace("Exception in transaction branch rollback, tx=" +
870                                toString(), e);
871                }
872             }
873             catch (Exception JavaDoc ignore)
874             {
875                if (trace)
876                   log.trace("Ignoring exception in replayCompletion, tx=" +
877                             toString(), ignore);
878             }
879          }
880          else
881             log.warn("Error reconstructing TransactionImpl instance for tx=" +
882                      toString() + " -- registeredResource not set.\n" +
883                      "The remote coordinator will NOT be contacted.");
884       }
885       else if (status == Status.STATUS_COMMITTING)
886       {
887          lock();
888          try
889          {
890             retryCommitRemoteResources();
891             
892             if (preparedXAWorkList.size() > 0)
893             {
894                // Keep this TransactionImpl around to
895
// commit its XAResources at a later time.
896
commitXAResourcesAfterTimeout();
897             }
898             else if (remoteResourcesToRetry == 0 && heuristicCode == HEUR_NONE)
899             {
900                // This TransactionImpl is not needed anymore.
901
completeTransaction();
902             }
903             else
904             {
905                // Do nothing. Just keep this TransactionImpl around to
906
// receive replayCompletion calls from remote resources.
907
}
908          }
909          finally
910          {
911             unlock();
912          }
913       }
914       else if (status == Status.STATUS_ROLLING_BACK)
915       {
916          try
917          {
918             rollbackResourcesAndCompleteTransaction();
919          }
920          finally
921          {
922             unlock();
923          }
924       }
925    }
926    
927    /**
928     * Constructor to recreate a foreign transaction that entered this virtual
929     * machine through JCA transaction inflow. This constructor is intended to
930     * be called at recovery time, for recreating transactions that were in the
931     * prepared state when the server crashed.
932     *
933     * @param localId the local id of a foreign transaction that entered this
934     * virtual machine in a transaction propagation context and is
935     * propagated along with a remote method invocation and is in
936     * the prepared state
937     * @param inboundFormatId format id part of the foreign <code>Xid</code>
938     * @param globalTransactionId global id part of the foreign <code>Xid</code>
939     * @param inboundBranchQualifier the branch qualifier part of the foreign
940     * <code>Xid</code>
941     * @param preparedXAWorkList a list of <code>org.jboss.tm.XAWork</code>
942     * instances containing one element for each
943     * <code>XAResource</code> enlisted with the transaction
944     * @param resources an array with stringfied references for the remote
945     * <code>Resource</code>s enlisted with the transaction
946     * @param completionHandler the
947     * <code>org.jboss.tm.recovery.TxCompletionHandler</code> to be
948     * notifed when the second phase of the 2PC completes
949     * @param heurData either null or a <code>LogRecord.HeurData</code> instance
950     * contaning information on a locally-detected heuristic hazard.
951     */

952    TransactionImpl(long localId,
953                    int inboundFormatId,
954                    byte[] globalTransactionId,
955                    byte[] inboundBranchQualifier,
956                    List JavaDoc preparedXAWorkList,
957                    String JavaDoc[] resources,
958                    TxCompletionHandler completionHandler,
959                    LogRecord.HeurData heurData)
960    {
961       foreignTx = true;
962       GlobalId globalId = new GlobalId(inboundFormatId, globalTransactionId);
963       xid = xidFactory.recreateXid(localId, globalId);
964       if (heurData == null)
965          status = Status.STATUS_PREPARED;
966       else
967       {
968          status = heurData.transactionStatus;
969          heuristicCode = heurData.heuristicStatusCode;
970          heuristicHazard = heurData.locallyDetectedHeuristicHazard;
971       }
972       this.inboundBranchQualifier = inboundBranchQualifier;
973       this.completionHandler = completionHandler;
974
975       for (Iterator JavaDoc it = preparedXAWorkList.iterator(); it.hasNext(); )
976       {
977          XAWork preparedXAWork = (XAWork) it.next();
978          EnlistedXAResource r = new EnlistedXAResource(preparedXAWork);
979          xaResources.add(r);
980       }
981       
982       for (int i = 0; i < resources.length; i++)
983       {
984          EnlistedRemoteResource resource =
985             new EnlistedRemoteResource(stringToResource(resources[i]), true);
986          remoteResources.add(resource);
987       }
988       
989       if (status == Status.STATUS_COMMITTING)
990       {
991          lock();
992          try
993          {
994             retryCommitRemoteResources();
995             
996             if (xaResourcesToRetry > 0)
997             {
998                // Keep this TransactionImpl around to
999
// commit its XAResources at a later time.
1000
commitXAResourcesAfterTimeout();
1001            }
1002            else if (remoteResourcesToRetry == 0 && heuristicCode == HEUR_NONE)
1003            {
1004               // This TransactionImpl is not needed anymore.
1005
completeTransaction();
1006            }
1007            else
1008            {
1009               // Do nothing. Just keep this TransactionImpl around to
1010
// receive replayCompletion calls from remote resources.
1011
}
1012         }
1013         finally
1014         {
1015            unlock();
1016         }
1017      }
1018      else if (status == Status.STATUS_ROLLING_BACK)
1019      {
1020         try
1021         {
1022            rollbackResourcesAndCompleteTransaction();
1023         }
1024         finally
1025         {
1026            unlock();
1027         }
1028      }
1029   }
1030   
1031   /**
1032    * Constructor to recreate a transaction that is in a heuristically
1033    * completed state (either committed or rolledback). This constructor is
1034    * intended to be called at recovery time, for recreating heuristically
1035    * completed transactions that were not yet forgotten when the server
1036    * crashed.
1037    *
1038    * @param heurData an instance of <code>LogRecord.HeurData</code> with
1039    * information on the heuristically completed transaction
1040    * @param xaResourcesWithHeuristics a list of
1041    * <code>org.jboss.tm.XAWork</code>
1042    * instances containing one element for each
1043    * <code>XAResource</code> that is in a a heuristic status
1044    * @param completionHandler the
1045    * <code>org.jboss.tm.recovery.TxCompletionHandler</code> to be
1046    * notifed when the second phase of the 2PC completes.
1047    */

1048   TransactionImpl(LogRecord.HeurData heurData,
1049                   List JavaDoc xaResourcesWithHeuristics,
1050                   TxCompletionHandler completionHandler)
1051   {
1052      foreignTx = heurData.foreignTx;
1053      status = heurData.transactionStatus;
1054      
1055      if (status != Status.STATUS_COMMITTING &&
1056            status != Status.STATUS_COMMITTED &&
1057            status != Status.STATUS_ROLLING_BACK &&
1058            status != Status.STATUS_ROLLEDBACK)
1059         throw new RuntimeException JavaDoc("Attempt to recreate heuristically " +
1060                                    "completed transaction with status " +
1061                                    TxUtils.getStatusAsString(status) + ". " +
1062                                    "Must be STATUS_COMMITTING, " +
1063                                    "STATUS_COMMITTED, STATUS_ROLLING_BACK, " +
1064                                    "or STATUS_ROLLEDBACK.");
1065      if (!foreignTx)
1066         xid = xidFactory.recreateXid(heurData.localTransactionId);
1067      else
1068      {
1069         GlobalId globalId = new GlobalId(heurData.formatId,
1070                                          heurData.globalTransactionId);
1071         xid = xidFactory.recreateXid(heurData.localTransactionId, globalId);
1072      }
1073      inboundBranchQualifier = heurData.inboundBranchQualifier;
1074      this.completionHandler = completionHandler;
1075      heuristicCode = heurData.heuristicStatusCode;
1076      if (!xaResourcesWithHeuristics.isEmpty())
1077      {
1078         xaResourcesWithHeuristicDecisions = new ArrayList JavaDoc();
1079         for (Iterator JavaDoc it = xaResourcesWithHeuristics.iterator();
1080              it.hasNext(); )
1081         {
1082            XAWork xaWork = (XAWork) it.next();
1083            xaWork.xaResourceAccess.release();
1084            EnlistedXAResource r = new EnlistedXAResource(
1085                     xaWork.res,
1086                     xaWork.xid,
1087                     (heurData.transactionStatus == Status.STATUS_COMMITTED));
1088            xaResources.add(r);
1089            xaResourcesWithHeuristicDecisions.add(r);
1090         }
1091      }
1092      if (heurData.remoteResourceHeuristics != null &&
1093            heurData.remoteResourceHeuristics.length > 0)
1094      {
1095         remoteResourcesWithHeuristicDecisions = new ArrayList JavaDoc();
1096         for (int i = 0; i < heurData.remoteResourceHeuristics.length; i++)
1097         {
1098            HeuristicStatus heurStatus = heurData.remoteResourceHeuristics[i];
1099            EnlistedRemoteResource r =
1100               new EnlistedRemoteResource(
1101                                       stringToResource(heurStatus.resourceRef),
1102                                       true, /* committed */
1103                                       heurStatus.code);
1104            remoteResources.add(r);
1105            remoteResourcesWithHeuristicDecisions.add(r);
1106         }
1107      }
1108   }
1109
1110   /**
1111    * Returns true if this is a foreign transaction and false if this is a
1112    * locally started transaction.
1113    *
1114    * @return true for a transaction that has been imported either through a
1115    * DTM/OTS transaction propagation context or through JCA
1116    * transaction inflow, and false for a locally started transaction.
1117    */

1118   public boolean isImported()
1119   {
1120      return foreignTx;
1121   }
1122
1123   // Implements TimeoutTarget --------------------------------------
1124

1125   /**
1126    * Called when our timeout expires.
1127    */

1128   public void timedOut(Timeout timeout)
1129   {
1130      lock();
1131      try
1132      {
1133         log.warn("Transaction " + toString() + " timed out." +
1134                  " status=" + TxUtils.getStatusAsString(status));
1135
1136         if (this.timeout == null)
1137            return; // Don't race with timeout cancellation.
1138
this.timeout = null;
1139
1140         switch (status)
1141         {
1142         case Status.STATUS_ROLLEDBACK:
1143         case Status.STATUS_COMMITTED:
1144         case Status.STATUS_NO_TRANSACTION:
1145            return; // Transaction done.
1146

1147         case Status.STATUS_ROLLING_BACK:
1148            return; // Will be done shortly.
1149

1150         case Status.STATUS_PREPARED:
1151         case Status.STATUS_COMMITTING:
1152            return; // Timeout expiration has no effect anymore.
1153

1154         case Status.STATUS_ACTIVE:
1155            status = Status.STATUS_MARKED_ROLLBACK;
1156            // fall through..
1157
case Status.STATUS_MARKED_ROLLBACK:
1158            // don't rollback for now, this messes up with the TxInterceptor.
1159
interruptThreads();
1160            return;
1161
1162         case Status.STATUS_PREPARING:
1163            status = Status.STATUS_MARKED_ROLLBACK;
1164            return; // commit will fail
1165

1166         default:
1167            log.warn("Unknown status at timeout, tx=" + toString());
1168            return;
1169         }
1170      }
1171      finally
1172      {
1173         unlock();
1174      }
1175   }
1176
1177   // Implements Transaction ----------------------------------------
1178

1179   public void commit()
1180           throws RollbackException JavaDoc,
1181                  HeuristicMixedException JavaDoc,
1182                  HeuristicRollbackException JavaDoc,
1183                  java.lang.SecurityException JavaDoc,
1184                  java.lang.IllegalStateException JavaDoc,
1185                  SystemException JavaDoc
1186   {
1187      lock();
1188      try
1189      {
1190         if (trace)
1191            log.trace("Committing, tx=" + toString() +
1192                      ", status=" + TxUtils.getStatusAsString(status));
1193
1194         beforePrepare();
1195
1196         if (status == Status.STATUS_ACTIVE)
1197         {
1198            switch (getCommitStrategy())
1199            {
1200            case 0:
1201               // Zero phase commit is really fast ;-)
1202
if (trace)
1203                  log.trace("Zero phase commit " + toString() +
1204                            ": No resources.");
1205               status = Status.STATUS_COMMITTED;
1206               break;
1207            case 1:
1208               // One phase commit
1209
if (trace)
1210                  log.trace("One phase commit " + toString() +
1211                            ": One resource.");
1212               commitResources(true);
1213               break;
1214            default:
1215               // Two phase commit
1216
if (trace)
1217                  log.trace("Two phase commit " + toString() +
1218                            ": Many resources.");
1219
1220               if (!prepareResources())
1221               {
1222                  boolean commitDecision =
1223                        status == Status.STATUS_PREPARED &&
1224                           (heuristicCode == HEUR_NONE ||
1225                                 heuristicCode == XAException.XA_HEURCOM);
1226
1227                  if (commitDecision)
1228                  {
1229                     // Save decision to stable storage
1230
// for recovery after system crash.
1231
try
1232                     {
1233                        RecoveryLogger logger =
1234                           TxManager.getInstance().getRecoveryLogger();
1235                        if (logger != null)
1236                        {
1237                           completionHandler = logger.saveCommitDecision(
1238                                    xid.getLocalIdValue(),
1239                                    getStringfiedRemoteResourcesThatVotedCommit());
1240                        }
1241                     }
1242                     catch (Throwable JavaDoc e)
1243                     {
1244                        if (e instanceof RecoveryTestingException)
1245                           throw (RecoveryTestingException) e;
1246                        log.warn("FAILED WHEN WRITING COMMIT RECORD."
1247                                 + " Rolling back now.", e);
1248                        status = Status.STATUS_MARKED_ROLLBACK;
1249                        cause = e;
1250                     }
1251                     cancelTimeout();
1252                     if (status == Status.STATUS_PREPARED)
1253                     {
1254                        try
1255                        {
1256                           commitResources(false);
1257                        }
1258                        catch (Throwable JavaDoc e)
1259                        {
1260                           if (e instanceof RecoveryTestingException)
1261                              throw (RecoveryTestingException) e;
1262                           log.warn("Unexpected exception in commitResources:",
1263                                    e);
1264                        }
1265                     }
1266                  }
1267               }
1268               else
1269                  status = Status.STATUS_COMMITTED; // all was read-only
1270
}
1271         }
1272
1273         if (status == Status.STATUS_COMMITTING)
1274         {
1275            // Keep this TransactionImpl around.
1276
if (xaResourcesToRetry > 0)
1277               commitXAResourcesAfterTimeout();
1278            checkHeuristicsButDoNotThrowHeuristicHazard();
1279         }
1280         else if (status != Status.STATUS_COMMITTED)
1281         {
1282            Throwable JavaDoc causedByThrowable = cause;
1283            rollbackResourcesAndCompleteTransaction();
1284
1285            // throw jboss rollback exception with the saved off cause
1286
throw new JBossRollbackException("Unable to commit, tx=" +
1287                                             toString() + ", status=" +
1288                                             TxUtils.getStatusAsString(status),
1289                                             causedByThrowable);
1290         }
1291         else /* (status == Status.STATUS_COMMITTED) */
1292         {
1293            cancelTimeout();
1294            doAfterCompletion();
1295            checkHeuristicsButDoNotThrowHeuristicHazard();
1296            instanceDone();
1297            
1298            if (trace)
1299               log.trace("Committed OK, tx=" + toString());
1300         }
1301      }
1302      finally
1303      {
1304         unlock();
1305      }
1306   }
1307
1308   public void rollback()
1309           throws java.lang.IllegalStateException JavaDoc,
1310                  java.lang.SecurityException JavaDoc,
1311                  SystemException JavaDoc
1312   {
1313      lock();
1314      try
1315      {
1316
1317         if (trace)
1318            log.trace("rollback(): Entered, tx=" + toString() +
1319                      ", status=" + TxUtils.getStatusAsString(status));
1320
1321         checkWork();
1322
1323         switch (status)
1324         {
1325         case Status.STATUS_ACTIVE:
1326            status = Status.STATUS_MARKED_ROLLBACK;
1327            // fall through..
1328
case Status.STATUS_MARKED_ROLLBACK:
1329            endResources();
1330            rollbackResourcesAndCompleteTransaction();
1331            // Cannot throw heuristic exception, so we just have to
1332
// clear the heuristics without reporting.
1333
heuristicCode = HEUR_NONE;
1334            return;
1335         case Status.STATUS_PREPARING:
1336            // Set status to avoid race with prepareResources().
1337
status = Status.STATUS_MARKED_ROLLBACK;
1338            return; // commit() will do rollback.
1339
default:
1340            throw new IllegalStateException JavaDoc("Cannot rollback(), tx=" +
1341                                            toString() + ", status=" +
1342                                            TxUtils.getStatusAsString(status));
1343         }
1344      }
1345      finally
1346      {
1347         Thread.interrupted();// clear timeout that did an interrupt
1348
unlock();
1349      }
1350   }
1351
1352   public boolean delistResource(XAResource JavaDoc xaRes, int flag)
1353           throws java.lang.IllegalStateException JavaDoc,
1354                  SystemException JavaDoc
1355   {
1356      if (xaRes == null)
1357         throw new IllegalArgumentException JavaDoc("null xaRes tx=" + toString());
1358      if (flag != XAResource.TMSUCCESS &&
1359          flag != XAResource.TMSUSPEND &&
1360          flag != XAResource.TMFAIL)
1361         throw new IllegalArgumentException JavaDoc("Bad flag: " + flag +
1362                                            " tx=" + toString());
1363
1364      lock();
1365      try
1366      {
1367         if (trace)
1368            log.trace("delistResource(): Entered, tx=" + toString() +
1369                      ", status=" + TxUtils.getStatusAsString(status));
1370
1371         EnlistedXAResource resource = findResource(xaRes);
1372         if (resource == null)
1373            throw new IllegalArgumentException JavaDoc("xaRes not enlisted " + xaRes);
1374
1375         switch (status)
1376         {
1377         case Status.STATUS_ACTIVE:
1378         case Status.STATUS_MARKED_ROLLBACK:
1379            break;
1380         case Status.STATUS_PREPARING:
1381            throw new IllegalStateException JavaDoc("Already started preparing, tx=" +
1382                                            toString());
1383         case Status.STATUS_ROLLING_BACK:
1384            throw new IllegalStateException JavaDoc("Already started rolling back, tx="
1385                                            + toString());
1386         case Status.STATUS_PREPARED:
1387            throw new IllegalStateException JavaDoc("Already prepared, tx=" +
1388                                            toString());
1389         case Status.STATUS_COMMITTING:
1390            throw new IllegalStateException JavaDoc("Already started committing, tx=" +
1391                                            toString());
1392         case Status.STATUS_COMMITTED:
1393            throw new IllegalStateException JavaDoc("Already committed, tx=" +
1394                                            toString());
1395         case Status.STATUS_ROLLEDBACK:
1396            throw new IllegalStateException JavaDoc("Already rolled back, tx=" +
1397                                            toString());
1398         case Status.STATUS_NO_TRANSACTION:
1399            throw new IllegalStateException JavaDoc("No transaction, tx=" + toString());
1400         case Status.STATUS_UNKNOWN:
1401            throw new IllegalStateException JavaDoc("Unknown state, tx=" + toString());
1402         default:
1403            throw new IllegalStateException JavaDoc("Illegal status: " +
1404                                            TxUtils.getStatusAsString(status) +
1405                                            ", tx=" + toString());
1406         }
1407
1408         try
1409         {
1410            return resource.delistResource(xaRes, flag);
1411         }
1412         catch (XAException JavaDoc xae)
1413         {
1414            logXAException(xae);
1415            status = Status.STATUS_MARKED_ROLLBACK;
1416            cause = xae;
1417            return false;
1418         }
1419      }
1420      finally
1421      {
1422         unlock();
1423      }
1424   }
1425
1426   public boolean enlistResource(XAResource JavaDoc xaRes)
1427           throws RollbackException JavaDoc,
1428                  java.lang.IllegalStateException JavaDoc,
1429                  SystemException JavaDoc
1430   {
1431      if (xaRes == null)
1432         throw new IllegalArgumentException JavaDoc("null xaRes, tx=" + toString());
1433
1434      lock();
1435      try
1436      {
1437         if (trace)
1438            log.trace("enlistResource(): Entered, tx=" + toString() +
1439                      ", status=" + TxUtils.getStatusAsString(status) +
1440                      ", xaRes=" + xaRes);
1441
1442         checkStatus();
1443
1444         if (resourcesEnded)
1445            throw new IllegalStateException JavaDoc("Too late to enlist resources, tx="
1446                                            + toString());
1447
1448         // Add resource
1449
try
1450         {
1451            EnlistedXAResource resource = findResource(xaRes);
1452
1453            // Existing resource
1454
if (resource != null)
1455            {
1456               if (resource.isEnlisted())
1457               {
1458                  if (trace)
1459                     log.trace("Already enlisted: tx=" + toString() +
1460                               ", status=" + TxUtils.getStatusAsString(status) +
1461                               ", xaRes=" + xaRes);
1462                  return true; // already enlisted
1463
}
1464               if (resource.isDelisted(xaRes))
1465               // this is a resource that returns false on all calls to
1466
// isSameRM. Further, the last resource enlisted has
1467
// already been delisted, so it is time to enlist it again.
1468
resource = null;
1469               else
1470                  return resource.startResource();
1471            }
1472            
1473            // Register itself as a resource with the parent coordinator
1474
if (parentCoordinator != null && recoveryCoordinator == null)
1475               registerResourceWithParentCoordinator();
1476
1477            resource = findResourceManager(xaRes);
1478            if (resource != null)
1479            {
1480               // The xaRes is new. We register the xaRes with the Xid
1481
// that the RM has previously seen from this transaction,
1482
// and note that it has the same RM.
1483
resource = addResource(xaRes, resource.getXid(), resource);
1484               return resource.startResource();
1485            }
1486
1487            // New resource and new RM: Create a new transaction branch.
1488
resource = addResource(xaRes, createXidBranch(), null);
1489            return resource.startResource();
1490         }
1491         catch (XAException JavaDoc xae)
1492         {
1493            logXAException(xae);
1494            cause = xae;
1495            return false;
1496         }
1497      }
1498      finally
1499      {
1500         unlock();
1501      }
1502
1503   }
1504
1505   public int getStatus()
1506   {
1507      if (done)
1508         return Status.STATUS_NO_TRANSACTION;
1509      return status;
1510   }
1511
1512   public void registerSynchronization(Synchronization JavaDoc s)
1513           throws RollbackException JavaDoc,
1514                  java.lang.IllegalStateException JavaDoc,
1515                  SystemException JavaDoc
1516   {
1517      if (s == null)
1518         throw new IllegalArgumentException JavaDoc("Null synchronization, tx=" +
1519                                            toString());
1520
1521      lock();
1522      try
1523      {
1524         if (trace)
1525         {
1526            log.trace("registerSynchronization(): Entered, " +
1527                      "tx=" + toString() +
1528                      ", status=" + TxUtils.getStatusAsString(status));
1529         }
1530
1531         checkStatus();
1532
1533         if (syncCount == syncAllocSize)
1534         {
1535            // expand table
1536
syncAllocSize = 2 * syncAllocSize;
1537
1538            Synchronization JavaDoc[] sy = new Synchronization JavaDoc[syncAllocSize];
1539            System.arraycopy(sync, 0, sy, 0, syncCount);
1540            sync = sy;
1541         }
1542         sync[syncCount++] = s;
1543         
1544         // Register itself as a resource with the parent coordinator
1545
if (parentCoordinator != null && recoveryCoordinator == null)
1546            registerResourceWithParentCoordinator();
1547      }
1548      finally
1549      {
1550         unlock();
1551      }
1552   }
1553
1554   public void setRollbackOnly()
1555           throws java.lang.IllegalStateException JavaDoc,
1556                  SystemException JavaDoc
1557   {
1558      lock();
1559      try
1560      {
1561         if (trace)
1562            log.trace("setRollbackOnly(): Entered, tx=" + toString() +
1563                      ", status=" + TxUtils.getStatusAsString(status));
1564
1565         switch (status)
1566         {
1567         case Status.STATUS_ACTIVE:
1568            // Register itself as a resource with the parent coordinator
1569
if (parentCoordinator != null && recoveryCoordinator == null)
1570            {
1571               try
1572               {
1573                  registerResourceWithParentCoordinator();
1574               }
1575               catch (RollbackException JavaDoc e)
1576               {
1577                  log.warn("RollbackException in setRollbackOnly: " + e);
1578               }
1579            }
1580            // fall through..
1581
case Status.STATUS_PREPARING:
1582         case Status.STATUS_PREPARED:
1583            status = Status.STATUS_MARKED_ROLLBACK;
1584            // fall through..
1585
case Status.STATUS_MARKED_ROLLBACK:
1586         case Status.STATUS_ROLLING_BACK:
1587            return;
1588         case Status.STATUS_COMMITTING:
1589            throw new IllegalStateException JavaDoc("Already started committing, tx=" +
1590                                            toString());
1591         case Status.STATUS_COMMITTED:
1592            throw new IllegalStateException JavaDoc("Already committed, tx=" +
1593                                            toString());
1594         case Status.STATUS_ROLLEDBACK:
1595            throw new IllegalStateException JavaDoc("Already rolled back, tx=" +
1596                                            toString());
1597         case Status.STATUS_NO_TRANSACTION:
1598            throw new IllegalStateException JavaDoc("No transaction, tx=" + toString());
1599         case Status.STATUS_UNKNOWN:
1600            throw new IllegalStateException JavaDoc("Unknown state, tx=" + toString());
1601         default:
1602            throw new IllegalStateException JavaDoc("Illegal status: " +
1603                                            TxUtils.getStatusAsString(status) +
1604                                            ", tx=" + toString());
1605         }
1606      }
1607      finally
1608      {
1609         unlock();
1610      }
1611   }
1612
1613   // Public methods called by DTM/OTS servants ---------------------
1614

1615   /**
1616    * Enlists a remote DTM/OTS resource in this transaction. Called by the
1617    * DTM/OTS servants to implement the register resource method of the
1618    * DTM/OTS coordinator interface.
1619    *
1620    * @param remoteRes a remote DTM/OTS resource to be enlisted in this
1621    * transaction
1622    * @throws RollbackException
1623    * @throws java.lang.IllegalStateException
1624    *
1625    */

1626   public void enlistRemoteResource(Resource JavaDoc remoteRes)
1627           throws RollbackException JavaDoc,
1628                  java.lang.IllegalStateException JavaDoc
1629   {
1630      if (remoteRes == null)
1631         throw new IllegalArgumentException JavaDoc("null remoteRes, tx=" + toString());
1632
1633      lock();
1634      try
1635      {
1636         if (trace)
1637            log.trace("enlistRemoteResource(): Entered, tx=" + toString() +
1638                      ", status=" + TxUtils.getStatusAsString(status));
1639
1640         checkStatus();
1641
1642         if (resourcesEnded)
1643            throw new IllegalStateException JavaDoc("Too late to enlist resources, tx="
1644                                            + toString());
1645
1646         // Register itself as a resource with the parent coordinator
1647
if (parentCoordinator != null && recoveryCoordinator == null)
1648            registerResourceWithParentCoordinator();
1649
1650         // Add resource
1651
EnlistedRemoteResource resource =
1652                 new EnlistedRemoteResource(remoteRes);
1653         remoteResources.add(resource);
1654      }
1655      finally
1656      {
1657         unlock();
1658      }
1659   }
1660
1661   /**
1662    * Prepare an external transaction
1663    *
1664    * @param inboundXid a foreign Xid that entered this VM via JCA transaction
1665    * inflow, or null in the case of an external transaction that
1666    * entered this VM in a transaction context propagated along with a
1667    * remote request.
1668    * @return XAResource.XA_RDONLY or XAResource.XA_OK
1669    */

1670   public int prepare(Xid JavaDoc inboundXid)
1671           throws HeuristicHazardException,
1672                  HeuristicMixedException JavaDoc,
1673                  HeuristicRollbackException JavaDoc,
1674                  RollbackException JavaDoc
1675   {
1676      lock();
1677      try
1678      {
1679         if (trace)
1680            log.trace("Preparing, tx=" + toString() +
1681                      ", status=" + TxUtils.getStatusAsString(status));
1682
1683         checkWork();
1684
1685         beforePrepare();
1686
1687         if (status == Status.STATUS_ACTIVE)
1688         {
1689            switch (getCommitStrategy())
1690            {
1691            case 0:
1692               // Nothing to do, but we must be careful with synchronizations
1693
if (trace)
1694                  log.trace("Prepare foreign tx=" + toString() +
1695                            ": No resources.");
1696               if (syncCount == 0)
1697               {
1698                  // No Synchronizations registered: we can vote read-only
1699
status = Status.STATUS_COMMITTED;
1700                  completeTransaction();
1701                  return XAResource.XA_RDONLY;
1702               }
1703               else
1704               { // There is at least one Synchronization registered:
1705
// vote commit so that afterCompletion gets called
1706
status = Status.STATUS_PREPARED;
1707                  return XAResource.XA_OK;
1708               }
1709            default:
1710               // Two phase commit
1711
if (trace)
1712                  log.trace("Prepare foreign tx=" + toString() +
1713                            ": one or more resources.");
1714                if (!prepareResources())
1715                {
1716                   boolean commitDecision =
1717                          status == Status.STATUS_PREPARED &&
1718                          (heuristicCode == HEUR_NONE ||
1719                           heuristicCode == XAException.XA_HEURCOM);
1720                   
1721                   if (commitDecision)
1722                   {
1723                      // Save decision to stable storage
1724
// for recovery after system crash.
1725
try
1726                      {
1727                         RecoveryLogger logger =
1728                            TxManager.getInstance().getRecoveryLogger();
1729                         if (logger != null)
1730                         {
1731                            if (inboundXid == null)
1732                            {
1733                               completionHandler = logger.savePrepareDecision(
1734                                  xid.getLocalIdValue(),
1735                                  xid.getFormatId(),
1736                                  xid.getGlobalTransactionId(),
1737                                  recoveryCoordinatorToString(recoveryCoordinator),
1738                                  getStringfiedRemoteResourcesThatVotedCommit());
1739                            }
1740                            else
1741                            {
1742                               completionHandler = logger.savePrepareDecision(
1743                                  xid.getLocalIdValue(),
1744                                  inboundXid,
1745                                  getStringfiedRemoteResourcesThatVotedCommit());
1746                            }
1747                         }
1748                      }
1749                      catch (Throwable JavaDoc e)
1750                      {
1751                         if (e instanceof RecoveryTestingException)
1752                            throw (RecoveryTestingException) e;
1753                         log.warn("FAILED WHEN WRITING PREPARE RECORD."
1754                                  + " Rolling back now.");
1755                      }
1756                   }
1757                }
1758                else
1759                {
1760                   if (syncCount == 0)
1761                   {
1762                      // No Synchronizations registered: we can vote read-only
1763
if (trace)
1764                         log.trace("Prepared foreign tx=" + toString() +
1765                                   ": All readonly," +
1766                                   " no Synchronizations registered");
1767                      status = Status.STATUS_COMMITTED;
1768                      completeTransaction();
1769                      return XAResource.XA_RDONLY;
1770                   }
1771                   else
1772                   { // There is at least one Synchronization registered:
1773
// vote commit so that afterCompletion gets called
1774
if (trace)
1775                         log.trace("Prepared foreign tx=" + toString() +
1776                                   ": All readonly, but there is at least" +
1777                                   " one Synchronization registered");
1778                      status = Status.STATUS_PREPARED;
1779                      return XAResource.XA_OK;
1780                   }
1781                }
1782                break;
1783            }
1784         }
1785
1786         if (status != Status.STATUS_PREPARED)
1787         {
1788            // save off the cause throwable as
1789
// instanceDone resets it to null
1790
Throwable JavaDoc causedByThrowable = cause;
1791            rollbackResourcesAndCompleteTransaction();
1792
1793            // throw jboss rollback exception with the saved off cause
1794
throw new JBossRollbackException("Unable to prepare, tx=" +
1795                                             toString() + ", status=" +
1796                                             TxUtils.getStatusAsString(status),
1797                                             causedByThrowable);
1798         }
1799         
1800         if (inboundXid == null)
1801            createPreparedTimeout();
1802         
1803         // We are ok to commit
1804
return XAResource.XA_OK;
1805      }
1806      finally
1807      {
1808         unlock();
1809      }
1810   }
1811
1812   /**
1813    * Commit an external transaction
1814    *
1815    * @param onePhase whether the commit is one or two phase
1816    */

1817   public void commit(boolean onePhase)
1818           throws RollbackException JavaDoc,
1819                  HeuristicHazardException,
1820                  HeuristicMixedException JavaDoc,
1821                  HeuristicRollbackException JavaDoc,
1822                  SystemException JavaDoc
1823   {
1824      checkWork();
1825
1826      // One phase commit optimization
1827
if (onePhase)
1828      {
1829         commit();
1830         return;
1831      }
1832
1833      // Two phase
1834
lock();
1835      try
1836      {
1837         if (trace)
1838            log.trace("Committing two phase, tx=" + toString() +
1839                      ", status=" + TxUtils.getStatusAsString(status));
1840
1841         cancelPreparedTimeout();
1842         
1843         switch (status)
1844         {
1845         case Status.STATUS_PREPARING:
1846            throw new IllegalStateException JavaDoc("Still preparing, tx=" +
1847                                            toString());
1848         case Status.STATUS_ROLLING_BACK:
1849            throw new IllegalStateException JavaDoc("Already started rolling back, tx="
1850                                            + toString());
1851         case Status.STATUS_ROLLEDBACK:
1852            instanceDone();
1853            //checkHeuristics();
1854
throw new IllegalStateException JavaDoc("Already rolled back, tx=" +
1855                                            toString());
1856         case Status.STATUS_COMMITTING:
1857            if (trace)
1858               log.trace("Already committing, tx=" + toString());
1859            return;
1860         case Status.STATUS_COMMITTED:
1861            instanceDone();
1862            //checkHeuristics();
1863
if (trace)
1864               log.trace("Already committed, tx=" + toString());
1865            return;
1866         case Status.STATUS_NO_TRANSACTION:
1867            throw new IllegalStateException JavaDoc("No transaction, tx=" + toString());
1868         case Status.STATUS_UNKNOWN:
1869            throw new IllegalStateException JavaDoc("Unknown state, tx=" + toString());
1870         case Status.STATUS_MARKED_ROLLBACK:
1871            endResources();
1872            rollbackResourcesAndCompleteTransaction();
1873            checkHeuristics();
1874            throw new RollbackException JavaDoc("Already marked for rollback, tx=" +
1875                                        toString());
1876         case Status.STATUS_PREPARED:
1877            break;
1878         default:
1879            throw new IllegalStateException JavaDoc("Illegal status: " +
1880                                            TxUtils.getStatusAsString(status) +
1881                                            ", tx=" + toString());
1882         }
1883
1884         commitResources(false);
1885         
1886         if (status == Status.STATUS_COMMITTING)
1887         {
1888            // Keep this TransactionImpl around.
1889
if (xaResourcesToRetry > 0)
1890               commitXAResourcesAfterTimeout();
1891            checkHeuristics();
1892         }
1893         else if (status != Status.STATUS_COMMITTED)
1894         {
1895            Throwable JavaDoc causedByThrowable = cause;
1896
1897            // throw jboss rollback exception with the saved off cause
1898
throw new JBossRollbackException("Unable to commit, tx=" +
1899                                             toString() + ", status=" +
1900                                             TxUtils.getStatusAsString(status),
1901                                             causedByThrowable);
1902         }
1903         else /* (status == Status.STATUS_COMMITTED) */
1904         {
1905            cancelTimeout();
1906            doAfterCompletion();
1907            checkHeuristics();
1908            instanceDone();
1909
1910            if (trace)
1911               log.trace("Committed OK, tx=" + toString());
1912         }
1913      }
1914      finally
1915      {
1916         unlock();
1917      }
1918   }
1919
1920   /**
1921    * Rollback an external transaction
1922    */

1923   public void rollbackBranch()
1924           throws HeuristicCommitException JavaDoc,
1925                  HeuristicMixedException JavaDoc,
1926                  HeuristicHazardException,
1927                  java.lang.IllegalStateException JavaDoc
1928   {
1929      lock();
1930      try
1931      {
1932
1933         if (trace)
1934            log.trace("rollbackBranch(): Entered, tx=" + toString() +
1935                      ", status=" + TxUtils.getStatusAsString(status));
1936
1937         checkWork();
1938         cancelPreparedTimeout();
1939         
1940         switch (status)
1941         {
1942         case Status.STATUS_ACTIVE:
1943         case Status.STATUS_PREPARED:
1944            status = Status.STATUS_MARKED_ROLLBACK;
1945            // fall through..
1946
case Status.STATUS_MARKED_ROLLBACK:
1947            endResources();
1948            rollbackResourcesAndCompleteTransaction();
1949            switch (getFullHeuristicCode())
1950            {
1951            case XAException.XA_HEURHAZ:
1952               if (trace)
1953                  log.trace("Throwing HeuristicHazardException, tx=" +
1954                            toString() + ", status=" +
1955                            TxUtils.getStatusAsString(status));
1956               throw new HeuristicHazardException();
1957            case XAException.XA_HEURMIX:
1958               if (trace)
1959                  log.trace("Throwing HeuristicMixedException, tx=" +
1960                            toString() + ", status=" +
1961                            TxUtils.getStatusAsString(status));
1962               throw new HeuristicMixedException JavaDoc();
1963            case XAException.XA_HEURRB:
1964               if (trace)
1965                  log.trace("Not throwing HeuristicRollbackException, tx=" +
1966                            toString() + ", status=" +
1967                            TxUtils.getStatusAsString(status));
1968               break;
1969            case XAException.XA_HEURCOM:
1970               if (trace)
1971                  log.trace("Throwing HeuristicCommitException, tx=" +
1972                            toString() + ", status=" +
1973                            TxUtils.getStatusAsString(status));
1974               throw new HeuristicCommitException JavaDoc();
1975            }
1976            return;
1977         case Status.STATUS_PREPARING:
1978            // Set status to avoid race with prepareResources().
1979
status = Status.STATUS_MARKED_ROLLBACK;
1980            return; // commit() will do rollback.
1981
case Status.STATUS_ROLLEDBACK:
1982            if (trace)
1983               log.trace("Already rolledback, tx=" + toString());
1984            return;
1985         default:
1986            throw new IllegalStateException JavaDoc("Cannot rollback(), tx=" +
1987                                            toString() + ", status=" +
1988                                            TxUtils.getStatusAsString(status));
1989         }
1990      }
1991      finally
1992      {
1993         Thread.interrupted();// clear timeout that did an interrupt
1994
unlock();
1995      }
1996   }
1997   
1998   /**
1999    * Forgets the heuristic status of the transaction. This method should
2000    * be called on heuristically completed transactions.
2001    */

2002   public void forget()
2003   {
2004      lock();
2005      try {
2006         if (heuristicCode == HEUR_NONE && !heuristicHazard)
2007            return;
2008         
2009         if (status != Status.STATUS_COMMITTED &&
2010               status != Status.STATUS_ROLLEDBACK &&
2011               (status != Status.STATUS_COMMITTING || !heuristicHazard) &&
2012               (status != Status.STATUS_ROLLING_BACK || !heuristicHazard))
2013            return;
2014
2015         if (forgetResources())
2016         {
2017            // Clear heuristics status from stable storage
2018
RecoveryLogger logger = TxManager.getInstance().getRecoveryLogger();
2019            if (logger != null)
2020            {
2021               logger.clearHeuristicStatus(xid.getLocalIdValue());
2022               if (status == Status.STATUS_COMMITTED &&
2023                     completionHandler != null)
2024                  completionHandler.handleTxCompletion(xid.getLocalIdValue());
2025            }
2026            instanceDone();
2027         }
2028      }
2029      finally
2030      {
2031         unlock();
2032      }
2033   }
2034
2035   /**
2036    * If this transaction is in the committing state, this method starts
2037    * a thread that calls commit on the remote resources that were not yet
2038    * successful committed. It returns the current status of the transaction
2039    * (before any commit calls are issued).
2040    *
2041    * @return the current status of the transaction.
2042    */

2043   public int replayCompletion(final Resource JavaDoc r)
2044   {
2045      lock();
2046      try
2047      {
2048         if (status != Status.STATUS_MARKED_ROLLBACK &&
2049               status != Status.STATUS_ROLLING_BACK &&
2050               status != Status.STATUS_ROLLEDBACK)
2051         {
2052            final boolean pendingCommits =
2053               (status == Status.STATUS_COMMITTING &&
2054                     remoteResourcesToRetry > 0);
2055            
2056            Runnable JavaDoc runnable = new Runnable JavaDoc()
2057            {
2058               public void run()
2059               {
2060                  try
2061                  {
2062                     if (trace)
2063                        log.trace("Replay completion thread: " +
2064                                  " committing remote resource " + r);
2065                     r.commit();
2066                     
2067                     if (trace)
2068                        log.trace("Replay completion thread: " +
2069                                  " committed remote resource " + r);
2070                  }
2071                  catch (Throwable JavaDoc ignored)
2072                  {
2073                     if (trace)
2074                        log.trace("Replay completion thread: ignored exception"
2075                                  + " when committing remote resource " + r ,
2076                                  ignored);
2077                  }
2078                  
2079                  if (pendingCommits)
2080                  {
2081                     lock();
2082                     try
2083                     {
2084                        if (trace)
2085                           log.trace("Replay completion thread: calling " +
2086                                     "retryCommitRemoteResources");
2087                        
2088                        retryCommitRemoteResources();
2089                        if (xaResourcesToRetry == 0 &&
2090                              remoteResourcesToRetry == 0 &&
2091                              heuristicCode == HEUR_NONE)
2092                           completeTransaction();
2093                     }
2094                     finally
2095                     {
2096                        unlock();
2097                     }
2098                  }
2099                  if (trace)
2100                     log.trace("Replay completion thread: exiting");
2101               }
2102            };
2103            Thread JavaDoc t = new Thread JavaDoc(runnable, "replayCompletionThread");
2104            
2105            t.start();
2106         }
2107         return status;
2108      }
2109      finally
2110      {
2111         unlock();
2112      }
2113   }
2114   
2115   /**
2116    * DTM/OTS servants need this method to implement the get transaction
2117    * propagation context method of the coordinator interface.
2118    *
2119    * @param errorRollback throw a RollbackException if the transaction is
2120    * not active
2121    * @return the time left before this transaction time-out expires.
2122    * @throws RollbackException if the transaction is not active and
2123    * errorRollback is true.
2124    */

2125   public long getTimeLeftBeforeTimeout(boolean errorRollback)
2126           throws RollbackException JavaDoc
2127   {
2128      if (errorRollback && status != Status.STATUS_ACTIVE)
2129         throw new RollbackException JavaDoc("Transaction is not active: " +
2130                                     TxUtils.getStatusAsString(status));
2131      return (start + timeoutPeriod) - System.currentTimeMillis();
2132   }
2133
2134   public int getAssociatedThreadCount()
2135   {
2136      lock();
2137      try
2138      {
2139         return threads.size();
2140      }
2141      finally
2142      {
2143         unlock();
2144      }
2145   }
2146
2147   public Set JavaDoc getAssociatedThreads()
2148   {
2149      lock();
2150      try
2151      {
2152         return Collections.unmodifiableSet(threads);
2153      }
2154      finally
2155      {
2156         unlock();
2157      }
2158   }
2159
2160   public int hashCode()
2161   {
2162      return xid.hashCode();
2163   }
2164
2165   public String JavaDoc toString()
2166   {
2167      return "TransactionImpl:" + xidFactory.toString(xid);
2168   }
2169
2170   public boolean equals(Object JavaDoc obj)
2171   {
2172      if (obj != null && obj instanceof TransactionImpl)
2173         return getLocalIdValue() == (((TransactionImpl) obj).getLocalIdValue());
2174      return false;
2175   }
2176
2177
2178   /**
2179    * Returns the local id of this transaction. The local id is used as
2180    * a transaction propagation context within the JBoss server, and
2181    * in the TxManager for mapping local transaction ids to transactions.
2182    */

2183   public long getLocalIdValue()
2184   {
2185      return xid.getLocalIdValue();
2186   }
2187
2188   /**
2189    * Returns the local id of this transaction. The local id is used as
2190    * a transaction propagation context within the JBoss server, and
2191    * in the TxManager for mapping local transaction ids to transactions.
2192    */

2193   public LocalId getLocalId()
2194   {
2195      return xid.getLocalId();
2196   }
2197
2198   /**
2199    * Returns the global id of this transaction. Ths global id is used in
2200    * the TxManager, which keeps a map from global ids to transactions.
2201    */

2202   public GlobalId getGlobalId()
2203   {
2204      return xid.getTrulyGlobalId();
2205   }
2206
2207   /**
2208    * Returns the xid of this transaction.
2209    */

2210   public XidImpl getXid()
2211   {
2212      return xid;
2213   }
2214
2215   /**
2216    * Returns this transaction's DTM propagation context.
2217    */

2218   public TxPropagationContext getPropagationContext()
2219   {
2220      if (dtmPropagationContext == null)
2221      {
2222         Coordinator coordinatorToPropagate;
2223
2224         if (parentCoordinator == null || interpositionEnabled ||
2225             !Proxy.isProxyClass(parentCoordinator.getClass()))
2226         {
2227            // no parent coordinator, or interposition enabled, or
2228
// parent coordinator is a wrapped OTS coordinator:
2229
// propagate proxy to a local DTM coordinator
2230
coordinatorToPropagate =
2231            dtmCoordinatorFactory.createCoordinator(getLocalIdValue());
2232         }
2233         else
2234         {
2235            // parent coordinator is a DTM coordinator and interposition is
2236
// disabled: propagate the parent coordinator
2237
coordinatorToPropagate = parentCoordinator;
2238         }
2239
2240         // Create TxPropagationContext
2241
dtmPropagationContext =
2242         new TxPropagationContext(xid.getFormatId(),
2243                                  xid.getGlobalTransactionId(),
2244                                  0, /* timeout set below */
2245                                  coordinatorToPropagate,
2246                                  null /* null Terminator */);
2247      }
2248      
2249      // Update the timeout in the TPC and return the TPC
2250
try
2251      {
2252         dtmPropagationContext.timeout =
2253            TMUtil.divideAndRoundUp(getTimeLeftBeforeTimeout(true), 1000);
2254      }
2255      catch (RollbackException JavaDoc transactionNotActive)
2256      {
2257         if (status == Status.STATUS_MARKED_ROLLBACK)
2258            dtmPropagationContext.timeout = 0;
2259         else
2260            dtmPropagationContext = null;
2261      }
2262      return dtmPropagationContext;
2263   }
2264
2265   /**
2266    * Returns this transaction's OTS propagation context.
2267    */

2268   public Object JavaDoc getOTSPropagationContext()
2269   {
2270      if (otsPropagationContext == null)
2271      {
2272         if (parentCoordinator == null || interpositionEnabled ||
2273             Proxy.isProxyClass(parentCoordinator.getClass()))
2274         {
2275            // no parent coordinator, or interposition enabled, or
2276
// parent coordinator is a dynamic proxy (rather than a wrapped
2277
// OTS coordinator): propagate reference to a local OTS coordinator
2278
otsPropagationContext =
2279            otsContextFactory.createOTSContext(xid.getFormatId(),
2280                                               xid.getGlobalTransactionId(),
2281                                               getLocalIdValue());
2282         }
2283         else
2284         {
2285            // parent coordinator is a DTM coordinator and interposition is
2286
// disabled: propagate the parent coordinator
2287
otsPropagationContext =
2288            otsContextFactory.createOTSContext(xid.getFormatId(),
2289                                               xid.getGlobalTransactionId(),
2290                                               parentCoordinator);
2291         }
2292      }
2293      
2294      // Update the timeout in the TPC and return the TPC
2295
try
2296      {
2297         otsContextFactory.setTimeout(
2298               otsPropagationContext,
2299               TMUtil.divideAndRoundUp(getTimeLeftBeforeTimeout(true), 1000));
2300      }
2301      catch (RollbackException JavaDoc transactionNotActive)
2302      {
2303         if (status == Status.STATUS_MARKED_ROLLBACK)
2304            otsContextFactory.setTimeout(otsPropagationContext, 0);
2305         else
2306            otsPropagationContext = null;
2307      }
2308      return otsPropagationContext;
2309   }
2310   
2311   public void setHeuristicStatus(LogRecord.HeurData heurData)
2312   {
2313      lock();
2314      try
2315      {
2316         if (status != heurData.transactionStatus)
2317         {
2318            if (status == Status.STATUS_PREPARED)
2319               cancelPreparedTimeout();
2320            
2321            status = heurData.transactionStatus;
2322            
2323            if (status == Status.STATUS_COMMITTING)
2324            {
2325               retryCommitRemoteResources();
2326               
2327               if (xaResourcesToRetry > 0)
2328               {
2329                  // Keep this TransactionImpl around to
2330
// commit its XAResources at a later time.
2331
commitXAResourcesAfterTimeout();
2332               }
2333               else if (remoteResourcesToRetry == 0 && heuristicCode == HEUR_NONE)
2334               {
2335                  // This TransactionImpl is not needed anymore.
2336
completeTransaction();
2337               }
2338               else
2339               {
2340                  // Do nothing. Just keep this TransactionImpl around to
2341
// receive replayCompletion calls from remote resources.
2342
}
2343            }
2344            else if (status == Status.STATUS_ROLLING_BACK)
2345            {
2346               rollbackResourcesAndCompleteTransaction();
2347            }
2348         }
2349      }
2350      finally
2351      {
2352         unlock();
2353      }
2354   }
2355   
2356   // Package protected ---------------------------------------------
2357

2358   /**
2359    * Checks whether this transaction is a foreign transaction that entered
2360    * the server via JCA transaction inflow and is either in the prepared state
2361    * or in a heuristically completed state.
2362    *
2363    * @return true if this is a JCA inbound transaction that is either prepared
2364    * or heuristically completed (but not yet forgotten), false
2365    * otherwise.
2366    */

2367   boolean isPreparedOrHeuristicallyCompletedJCAInboundTx()
2368   {
2369      return inboundBranchQualifier != null
2370         && (status == Status.STATUS_PREPARED
2371               || (getFullHeuristicCode() != HEUR_NONE
2372                     && (status == Status.STATUS_COMMITTED
2373                           || status == Status.STATUS_ROLLEDBACK)));
2374   }
2375   
2376   /**
2377    * Gets the inbound <code>Xid</code> of a foreign transaction that entered
2378    * the server via JCA transaction inflow.
2379    *
2380    * @return the inbound <code>Xid</code>.
2381    */

2382   Xid JavaDoc getInboundXid()
2383   {
2384      return new Xid JavaDoc()
2385      {
2386         public int getFormatId()
2387         {
2388            return xid.getFormatId();
2389         }
2390
2391         public byte[] getGlobalTransactionId()
2392         {
2393            return xid.getGlobalTransactionId();
2394         }
2395
2396         public byte[] getBranchQualifier()
2397         {
2398            return (byte[])inboundBranchQualifier.clone();
2399         }
2400      };
2401   }
2402   
2403   void associateCurrentThread()
2404   {
2405      Thread.interrupted();
2406      lock();
2407      try
2408      {
2409         threads.add(Thread.currentThread());
2410      }
2411      finally
2412      {
2413         unlock();
2414      }
2415   }
2416
2417   void disassociateCurrentThread()
2418   {
2419      // Just a tidyup, no need to synchronize
2420
if (done)
2421      {
2422         threads.remove(Thread.currentThread());
2423      }
2424      else
2425      {
2426         // Removing the association for an active transaction
2427
lock();
2428         try
2429         {
2430            threads.remove(Thread.currentThread());
2431         }
2432         finally
2433         {
2434            unlock();
2435         }
2436      }
2437      Thread.interrupted();
2438   }
2439
2440   /**
2441    * Lock this instance.
2442    */

2443   synchronized void lock()
2444   {
2445      if (done)
2446         throw new IllegalStateException JavaDoc("Transaction has terminated, tx=" +
2447                                         toString());
2448
2449      Thread JavaDoc currentThread = Thread.currentThread();
2450      if (locked != null && locked != currentThread)
2451      {
2452         log.debug("Lock contention, tx=" + toString() +
2453                   " otherThread=" + locked);
2454         //DEBUG Thread.currentThread().dumpStack();
2455

2456         while (locked != null && locked != currentThread)
2457         {
2458            try
2459            {
2460               // Wakeup happens when:
2461
// - notify() is called from unlock()
2462
// - notifyAll is called from instanceDone()
2463
wait();
2464            }
2465            catch (InterruptedException JavaDoc ex)
2466            {
2467               // ignore
2468
}
2469
2470            if (done)
2471               throw new IllegalStateException JavaDoc(
2472                           "Transaction has now terminated, tx=" + toString());
2473         }
2474      }
2475
2476      locked = currentThread;
2477      ++lockDepth;
2478   }
2479
2480   /**
2481    * Unlock this instance.
2482    */

2483   synchronized void unlock()
2484   {
2485      Thread JavaDoc currentThread = Thread.currentThread();
2486      if (locked == null || locked != currentThread)
2487      {
2488         log.warn("Unlocking, but not locked, tx=" + toString() +
2489                  " otherThread=" + locked,
2490                  new Throwable JavaDoc("[Stack trace]"));
2491      }
2492      else
2493      {
2494         if (--lockDepth == 0)
2495         {
2496            locked = null;
2497            notify();
2498         }
2499      }
2500   }
2501   
2502   /**
2503    * Deactivate this transaction.
2504    */

2505   synchronized void deactivate()
2506   {
2507      lock();
2508      try
2509      {
2510         cancelTimeout();
2511         cancelPreparedTimeout();
2512         cancelXARetryTimeout();
2513      
2514         // Clear tables refering to external objects.
2515
// Even if a client holds on to this instance forever, the objects
2516
// that we have referenced may be garbage collected.
2517
parentCoordinator = null;
2518         sync = null;
2519         xaResources = null;
2520         remoteResources = null;
2521         transactionLocalMap.clear();
2522         threads.clear();
2523      
2524         // Set the status
2525
status = Status.STATUS_NO_TRANSACTION;
2526
2527         // Notify all threads waiting for the lock.
2528
notifyAll();
2529
2530         // set the done flag
2531
done = true;
2532      }
2533      finally
2534      {
2535         unlock();
2536      }
2537   }
2538
2539   /**
2540    * Get the work
2541    *
2542    * @return the work
2543    */

2544   Work JavaDoc getWork()
2545   {
2546      return work;
2547   }
2548
2549   /**
2550    * Set the work
2551    *
2552    * @param work the work
2553    * @throws WorkCompletedException with error code WorkException.TX_CONCURRENT_WORK_DISALLOWED
2554    * when work is already present for the xid or whose completion is in progress, only
2555    * the global part of the xid must be used for this check. Or with error code
2556    * WorkException.TX_RECREATE_FAILED if it is unable to recreate the transaction context
2557    */

2558   void setWork(Work JavaDoc work)
2559           throws WorkCompletedException JavaDoc
2560   {
2561      lock();
2562      try
2563      {
2564         if (work == null)
2565         {
2566            this.work = null;
2567            return;
2568         }
2569
2570         if (status == Status.STATUS_NO_TRANSACTION || status == Status.STATUS_UNKNOWN)
2571            throw new WorkCompletedException JavaDoc("The transaction is not active " +
2572                                             toString() + ": " +
2573                                             TxUtils.getStatusAsString(status),
2574                                             WorkException.TX_RECREATE_FAILED);
2575         else if (status != Status.STATUS_ACTIVE)
2576            throw new WorkCompletedException JavaDoc("Too late to start work " +
2577                                             toString() + ": " +
2578                                             TxUtils.getStatusAsString(status),
2579                                             WorkException.TX_CONCURRENT_WORK_DISALLOWED);
2580         else if (this.work != null)
2581            throw new WorkCompletedException JavaDoc("Already have work " + toString() +
2582                                             ": " + this.work,
2583                                             WorkException.TX_CONCURRENT_WORK_DISALLOWED);
2584
2585         this.work = work;
2586      }
2587      finally
2588      {
2589         unlock();
2590      }
2591   }
2592
2593   /**
2594    * Getter for property done.
2595    */

2596   boolean isDone()
2597   {
2598      return done || (heuristicCode != HEUR_NONE &&
2599                          (status == Status.STATUS_COMMITTING ||
2600                           status == Status.STATUS_COMMITTED ||
2601                           status == Status.STATUS_ROLLING_BACK ||
2602                           status == Status.STATUS_ROLLEDBACK));
2603   }
2604
2605   Object JavaDoc getTransactionLocalValue(TransactionLocal tlocal)
2606   {
2607      return transactionLocalMap.get(tlocal);
2608   }
2609
2610   void putTransactionLocalValue(TransactionLocal tlocal, Object JavaDoc value)
2611   {
2612      transactionLocalMap.put(tlocal, value);
2613   }
2614
2615   boolean containsTransactionLocal(TransactionLocal tlocal)
2616   {
2617      return transactionLocalMap.containsKey(tlocal);
2618   }
2619
2620   // Private -------------------------------------------------------
2621

2622   /**
2623    * Before prepare
2624    */

2625   private void beforePrepare()
2626           throws HeuristicMixedException JavaDoc,
2627                  HeuristicRollbackException JavaDoc,
2628                  RollbackException JavaDoc
2629   {
2630      checkIntegrity();
2631
2632      doBeforeCompletion();
2633
2634      if (trace)
2635         log.trace("Before completion done, tx=" + toString() +
2636                   ", status=" + TxUtils.getStatusAsString(status));
2637
2638      endResources();
2639   }
2640
2641   /**
2642    * Check the integrity of the transaction
2643    */

2644   private void checkIntegrity()
2645         throws HeuristicMixedException JavaDoc,
2646                HeuristicRollbackException JavaDoc,
2647                RollbackException JavaDoc
2648   {
2649      // Spec defined checks for the transaction in a valid state
2650
checkBeforeStatus();
2651
2652      TransactionIntegrity integrity = TxManager.getInstance().getTransactionIntegrity();
2653      if (integrity != null)
2654      {
2655         // Extra integrity checks
2656
unlock();
2657         try
2658         {
2659            integrity.checkTransactionIntegrity(this);
2660         }
2661         finally
2662         {
2663            lock();
2664         }
2665         
2666         // Recheck the transaction state
2667
checkBeforeStatus();
2668      }
2669   }
2670
2671   /**
2672    * Check the before status
2673    */

2674   private void checkBeforeStatus()
2675      throws HeuristicMixedException JavaDoc,
2676             HeuristicRollbackException JavaDoc,
2677             RollbackException JavaDoc
2678   {
2679      switch (status)
2680      {
2681      case Status.STATUS_PREPARING:
2682         throw new IllegalStateException JavaDoc("Already started preparing, tx=" +
2683                                         toString());
2684      case Status.STATUS_PREPARED:
2685         throw new IllegalStateException JavaDoc("Already prepared, tx=" + toString());
2686      case Status.STATUS_ROLLING_BACK:
2687         throw new IllegalStateException JavaDoc("Already started rolling back, tx=" +
2688                                         toString());
2689      case Status.STATUS_ROLLEDBACK:
2690         instanceDone();
2691         //checkHeuristics();
2692
throw new IllegalStateException JavaDoc("Already rolled back, tx=" +
2693                                         toString());
2694      case Status.STATUS_COMMITTING:
2695         throw new IllegalStateException JavaDoc("Already started committing, tx=" +
2696                                         toString());
2697      case Status.STATUS_COMMITTED:
2698         instanceDone();
2699         //checkHeuristics();
2700
throw new IllegalStateException JavaDoc("Already committed, tx=" + toString());
2701      case Status.STATUS_NO_TRANSACTION:
2702         throw new IllegalStateException JavaDoc("No transaction, tx=" + toString());
2703      case Status.STATUS_UNKNOWN:
2704         throw new IllegalStateException JavaDoc("Unknown state, tx=" + toString());
2705      case Status.STATUS_MARKED_ROLLBACK:
2706         endResources();
2707         rollbackResourcesAndCompleteTransaction();
2708         checkHeuristicsButDoNotThrowHeuristicHazard();
2709         throw new RollbackException JavaDoc("Already marked for rollback, tx=" +
2710                                     toString());
2711      case Status.STATUS_ACTIVE:
2712         break;
2713      default:
2714         throw new IllegalStateException JavaDoc("Illegal status: " +
2715                                         TxUtils.getStatusAsString(status) +
2716                                         ", tx=" + toString());
2717      }
2718   }
2719   
2720   /**
2721    * Complete the transaction
2722    */

2723   private void completeTransaction()
2724   {
2725      cancelTimeout();
2726      doAfterCompletion();
2727      instanceDone();
2728   }
2729
2730   /**
2731    * Checks if the transaction state allows calls to the methods
2732    * <code>enlistResource</code> and <code>registerSynchronization</code>
2733    *
2734    * @throws RollbackException
2735    * @throws IllegalStateException
2736    */

2737   private void checkStatus()
2738           throws RollbackException JavaDoc
2739   {
2740      switch (status)
2741      {
2742      case Status.STATUS_ACTIVE:
2743      case Status.STATUS_PREPARING:
2744         break;
2745      case Status.STATUS_PREPARED:
2746         throw new IllegalStateException JavaDoc("Already prepared, tx=" + toString());
2747      case Status.STATUS_COMMITTING:
2748         throw new IllegalStateException JavaDoc("Already started committing, tx=" +
2749                                         toString());
2750      case Status.STATUS_COMMITTED:
2751         throw new IllegalStateException JavaDoc("Already committed, tx=" + toString());
2752      case Status.STATUS_MARKED_ROLLBACK:
2753         throw new RollbackException JavaDoc("Already marked for rollback, tx=" +
2754                                     toString());
2755      case Status.STATUS_ROLLING_BACK:
2756         throw new RollbackException JavaDoc("Already started rolling back, tx=" +
2757                                     toString());
2758      case Status.STATUS_ROLLEDBACK:
2759         throw new RollbackException JavaDoc("Already rolled back, tx=" + toString());
2760      case Status.STATUS_NO_TRANSACTION:
2761         throw new IllegalStateException JavaDoc("No transaction, tx=" + toString());
2762      case Status.STATUS_UNKNOWN:
2763         throw new IllegalStateException JavaDoc("Unknown state, tx=" + toString());
2764      default:
2765         throw new IllegalStateException JavaDoc("Illegal status: " +
2766                                         TxUtils.getStatusAsString(status) +
2767                                         ", tx=" + toString());
2768      }
2769   }
2770
2771   /**
2772    * Interrupt all threads involved with transaction
2773    * This is called on timeout
2774    */

2775   private void interruptThreads()
2776   {
2777      TxManager manager = TxManager.getInstance();
2778      if (manager.isInterruptThreads())
2779      {
2780         HashSet JavaDoc clone = (HashSet JavaDoc) threads.clone();
2781         threads.clear();
2782         for (Iterator JavaDoc i = clone.iterator(); i.hasNext();)
2783         {
2784            Thread JavaDoc thread = (Thread JavaDoc) i.next();
2785            try
2786            {
2787               thread.interrupt();
2788            }
2789            catch (Throwable JavaDoc ignored)
2790            {
2791               if (trace)
2792                  log.trace("Ignored error interrupting thread: " +
2793                            thread, ignored);
2794            }
2795         }
2796      }
2797   }
2798
2799   private void logXAException(XAException JavaDoc xae)
2800   {
2801      log.warn("XAException: tx=" + toString() + " errorCode=" +
2802               TxUtils.getXAErrorCodeAsString(xae.errorCode), xae);
2803      if (xaExceptionFormatter != null)
2804         xaExceptionFormatter.formatXAException(xae, log);
2805   }
2806   
2807   /**
2808    * Mark this transaction as non-existing.
2809    */

2810   private synchronized void instanceDone()
2811   {
2812      TxManager manager = TxManager.getInstance();
2813
2814      if (status == Status.STATUS_COMMITTED)
2815         manager.incCommitCount();
2816      else
2817         manager.incRollbackCount();
2818
2819      // Clear tables refering to external objects.
2820
// Even if a client holds on to this instance forever, the objects
2821
// that we have referenced may be garbage collected.
2822
parentCoordinator = null;
2823      sync = null;
2824      xaResources = null;
2825      remoteResources = null;
2826      transactionLocalMap.clear();
2827      threads.clear();
2828      
2829      // Garbage collection
2830
manager.releaseTransactionImpl(this);
2831
2832      // Set the status
2833
status = Status.STATUS_NO_TRANSACTION;
2834
2835      // Notify all threads waiting for the lock.
2836
notifyAll();
2837
2838      // set the done flag
2839
done = true;
2840   }
2841
2842   /**
2843    * Cancel the timeout.
2844    * This will release the lock while calling out.
2845    */

2846   private void cancelTimeout()
2847   {
2848      if (timeout != null)
2849      {
2850         unlock();
2851         try
2852         {
2853            timeout.cancel();
2854         }
2855         catch (Exception JavaDoc e)
2856         {
2857            if (trace)
2858               log.trace("failed to cancel timeout, tx=" + toString(), e);
2859         }
2860         finally
2861         {
2862            lock();
2863         }
2864         timeout = null;
2865      }
2866   }
2867
2868   /**
2869    * Create prepared timeout.
2870    */

2871   private void createPreparedTimeout()
2872   {
2873      preparedTimeout = timeoutFactory.createTimeout(
2874            System.currentTimeMillis() +
2875                  TxManager.getInstance().getPreparedTimeoutMillis(),
2876            new TimeoutTarget()
2877            {
2878               public void timedOut(Timeout timeout)
2879               {
2880                  if (trace)
2881                     log.trace("Prepared timeout expired for tx=" +
2882                               TransactionImpl.this.toString() +
2883                               ", calling replayCompletion " +
2884                               "on the recovery coordinator");
2885                  try
2886                  {
2887                     recoveryCoordinator.replayCompletion(registeredResource);
2888                  }
2889                  catch (NoSuchObjectException JavaDoc noCoordinator)
2890                  {
2891                     if (trace)
2892                     {
2893                        log.trace("Exception in replayCompletion: " +
2894                                  "no coordinator for tx=" + toString(),
2895                                  noCoordinator);
2896                        log.trace("Rolling back transaction branch, tx=" +
2897                                  TransactionImpl.this.toString());
2898                     }
2899                     try
2900                     {
2901                        rollbackBranch();
2902                     }
2903                     catch (Exception JavaDoc e)
2904                     {
2905                        if (trace)
2906                           log.trace("Exception in transaction branch rollback,"
2907                                     + " tx=" + TransactionImpl.this.toString(),
2908                                     e);
2909                        
2910                     }
2911                  }
2912                  catch (Exception JavaDoc ignore)
2913                  {
2914                     if (trace)
2915                        log.trace("Ignoring exception in replayCompletion, tx="
2916                                  + TransactionImpl.this.toString(), ignore);
2917                  }
2918                  if (!done)
2919                  {
2920                     lock();
2921                     try
2922                     {
2923                        if (preparedTimeout != null)
2924                        {
2925                           preparedTimeout = timeoutFactory.createTimeout(
2926                                 System.currentTimeMillis() +
2927                                       TxManager.getInstance()
2928                                                .getPreparedTimeoutMillis(),
2929                                 this);
2930                        }
2931                     }
2932                     finally
2933                     {
2934                        unlock();
2935                     }
2936                  }
2937               }
2938            });
2939   }
2940
2941   /**
2942    * Cancel prepared timeout.
2943    */

2944   private void cancelPreparedTimeout()
2945   {
2946      if (preparedTimeout != null)
2947      {
2948         Timeout pt = preparedTimeout;
2949         preparedTimeout = null;
2950         
2951         unlock();
2952         try
2953         {
2954            pt.cancel();
2955         }
2956         catch (Exception JavaDoc e)
2957         {
2958            if (trace)
2959               log.trace("failed to cancel prepared timeout, tx=" + toString(),
2960                         e);
2961         }
2962         finally
2963         {
2964            lock();
2965         }
2966      }
2967   }
2968   
2969   /**
2970    * Return the resource for the given XAResource
2971    */

2972   private EnlistedXAResource findResource(XAResource JavaDoc xaRes)
2973   {
2974      // A linear search may seem slow, but please note that
2975
// the number of XA resources registered with a transaction
2976
// are usually low.
2977
// Note: This searches backwards intentionally! It ensures that
2978
// if this resource was enlisted multiple times, then the last one
2979
// will be returned. All others should be in the state RS_ENDED.
2980
// This allows ResourceManagers that always return false from isSameRM
2981
// to be enlisted and delisted multiple times.
2982
for (int idx = xaResources.size() - 1; idx >= 0; --idx)
2983      {
2984         EnlistedXAResource resource =
2985                 (EnlistedXAResource) xaResources.get(idx);
2986         if (xaRes == resource.getXAResource())
2987            return resource;
2988      }
2989
2990      return null;
2991   }
2992
2993   private EnlistedXAResource findResourceManager(XAResource JavaDoc xaRes)
2994           throws XAException JavaDoc
2995   {
2996      for (int i = 0; i < xaResources.size(); ++i)
2997      {
2998         EnlistedXAResource resource = (EnlistedXAResource) xaResources.get(i);
2999         if (resource.isResourceManager(xaRes))
3000            return resource;
3001      }
3002      return null;
3003   }
3004
3005   /**
3006    * Add a resource, expanding tables if needed.
3007    *
3008    * @param xaRes The new XA resource to add. It is assumed that the
3009    * resource is not already in the table of XA resources.
3010    * @param branchXid The Xid for the transaction branch that is to
3011    * be used for associating with this resource.
3012    * @param sameRMResource The resource of the first
3013    * XA resource having the same resource manager as
3014    * <code>xaRes</code>, or <code>null</code> if <code>xaRes</code>
3015    * is the first resource seen with this resource manager.
3016    * @return the new resource
3017    */

3018   private EnlistedXAResource addResource(XAResource JavaDoc xaRes,
3019                                          Xid JavaDoc branchXid,
3020                                          EnlistedXAResource sameRMResource)
3021   {
3022      EnlistedXAResource resource =
3023              new EnlistedXAResource(xaRes, branchXid, sameRMResource);
3024      xaResources.add(resource);
3025
3026      // Remember the first resource that wants the last resource gambit
3027
if (lastResource == null && xaRes instanceof LastResource)
3028         lastResource = resource;
3029
3030      return resource;
3031   }
3032
3033   /**
3034    * End Tx association for all resources.
3035    */

3036   private void endResources()
3037   {
3038      for (int idx = 0; idx < xaResources.size(); ++idx)
3039      {
3040         EnlistedXAResource resource =
3041                 (EnlistedXAResource) xaResources.get(idx);
3042         try
3043         {
3044            resource.endResource();
3045         }
3046         catch (XAException JavaDoc xae)
3047         {
3048            logXAException(xae);
3049            status = Status.STATUS_MARKED_ROLLBACK;
3050            cause = xae;
3051         }
3052      }
3053      resourcesEnded = true; // Too late to enlist new resources.
3054
}
3055
3056
3057   /**
3058    * Call synchronization <code>beforeCompletion()</code>.
3059    * This will release the lock while calling out.
3060    */

3061   private void doBeforeCompletion()
3062   {
3063      unlock();
3064      try
3065      {
3066         for (int i = 0; i < syncCount; i++)
3067         {
3068            try
3069            {
3070               if (trace)
3071                  log.trace("calling sync " + i + ", " + sync[i] +
3072                            " tx=" + toString());
3073
3074               sync[i].beforeCompletion();
3075            }
3076            catch (Throwable JavaDoc t)
3077            {
3078               if (trace)
3079                  log.trace("failed before completion " + sync[i], t);
3080
3081               status = Status.STATUS_MARKED_ROLLBACK;
3082
3083               // save the cause off so the user can inspect it
3084
cause = t;
3085               break;
3086            }
3087         }
3088      }
3089      finally
3090      {
3091         lock();
3092      }
3093   }
3094
3095   /**
3096    * Call synchronization <code>afterCompletion()</code>.
3097    * This will release the lock while calling out.
3098    */

3099   private void doAfterCompletion()
3100   {
3101      // Assert: Status indicates: Too late to add new synchronizations.
3102
unlock();
3103      try
3104      {
3105         for (int i = 0; i < syncCount; i++)
3106         {
3107            try
3108            {
3109               sync[i].afterCompletion(status);
3110            }
3111            catch (Throwable JavaDoc t)
3112            {
3113               if (trace)
3114                  log.trace("failed after completion " + sync[i], t);
3115            }
3116         }
3117      }
3118      finally
3119      {
3120         lock();
3121      }
3122   }
3123
3124   /**
3125    * Groups two heuristic codes into a composite code.
3126    *
3127    * @param code1 a heuristic code (one of
3128    * <code>XAException.XA_HEURxxx</code>)
3129    * @param code2 another heuristic code
3130    * @return the composite heuristic code.
3131    */

3132   private static int groupHeuristics(int code1, int code2)
3133   {
3134      switch (code2)
3135      {
3136      case XAException.XA_HEURMIX:
3137         code1 = XAException.XA_HEURMIX;
3138         break;
3139      case XAException.XA_HEURRB:
3140         if (code1 == HEUR_NONE)
3141            code1 = XAException.XA_HEURRB;
3142         else if (code1 == XAException.XA_HEURCOM ||
3143                  code1 == XAException.XA_HEURHAZ)
3144            code1 = XAException.XA_HEURMIX;
3145         break;
3146      case XAException.XA_HEURCOM:
3147         if (code1 == HEUR_NONE)
3148            code1 = XAException.XA_HEURCOM;
3149         else if (code1 == XAException.XA_HEURRB ||
3150                  code1 == XAException.XA_HEURHAZ)
3151            code1 = XAException.XA_HEURMIX;
3152         break;
3153      case XAException.XA_HEURHAZ:
3154         if (code1 == HEUR_NONE)
3155            code1 = XAException.XA_HEURHAZ;
3156         else if (code1 == XAException.XA_HEURCOM ||
3157                  code1 == XAException.XA_HEURRB)
3158            code1 = XAException.XA_HEURMIX;
3159         break;
3160      default:
3161         throw new IllegalArgumentException JavaDoc();
3162      }
3163
3164      return code1;
3165   }
3166
3167   /**
3168    * We got another heuristic.
3169    * <p/>
3170    * Promote <code>heuristicCode</code> if needed and remember to call forget
3171    * on the resource at a later time, upon request from the coordinator
3172    * or from a management application.
3173    *
3174    * This will release the lock while calling out.
3175    *
3176    * @param resource the resource of the XA resource that got a
3177    * heuristic in our internal tables, or <code>null</code>
3178    * if the heuristic came from here.
3179    * @param code the heuristic code, one of
3180    * <code>XAException.XA_HEURxxx</code>.
3181    */

3182   private void gotHeuristic(EnlistedResource resource, int code)
3183   {
3184      heuristicCode = groupHeuristics(heuristicCode, code);
3185      if (resource != null)
3186            resource.remember(code);
3187   }
3188   
3189   /**
3190    * Checks if a mixed decision happened and promote the
3191    * <code>heuristicCode</code> in that case.
3192    *
3193    * @return the <code>heuristicCode</code>.
3194    */

3195   private int consolidateHeuristics()
3196   {
3197      if (committedResources > 0 && rolledbackResources > 0)
3198         heuristicCode = XAException.XA_HEURMIX;
3199      
3200      return heuristicCode;
3201   }
3202   
3203   /**
3204    * Gets the full heuristic code, which includes an heuristic hazard if this
3205    * <code>TransactionImpl</code> could not reach a resource during the second
3206    * phase of 2PC.
3207    *
3208    * @return the full heuristic code, one of
3209    * <code>XAException.XA_HEURxxx</code>.
3210    */

3211   private int getFullHeuristicCode()
3212   {
3213      return (heuristicHazard) ?
3214                        groupHeuristics(heuristicCode, XAException.XA_HEURHAZ) :
3215                        heuristicCode;
3216   }
3217
3218   /**
3219    * Check for heuristics and throw exception if any found.
3220    */

3221   private void checkHeuristics()
3222           throws HeuristicHazardException,
3223                  HeuristicMixedException JavaDoc,
3224                  HeuristicRollbackException JavaDoc
3225   {
3226      if (committedResources > 0 && rolledbackResources > 0)
3227         heuristicCode = XAException.XA_HEURMIX;
3228      
3229      switch (getFullHeuristicCode())
3230      {
3231      case XAException.XA_HEURHAZ:
3232         if (trace)
3233            log.trace("Throwing HeuristicHazardException, tx=" + toString() +
3234                      ", status=" + TxUtils.getStatusAsString(status));
3235         throw new HeuristicHazardException();
3236      case XAException.XA_HEURMIX:
3237         if (trace)
3238            log.trace("Throwing HeuristicMixedException, tx=" + toString() +
3239                      ", status=" + TxUtils.getStatusAsString(status));
3240         throw new HeuristicMixedException JavaDoc();
3241      case XAException.XA_HEURRB:
3242         if (trace)
3243            log.trace("Throwing HeuristicRollbackException, tx=" + toString() +
3244                      ", status=" + TxUtils.getStatusAsString(status));
3245         throw new HeuristicRollbackException JavaDoc();
3246      case XAException.XA_HEURCOM:
3247         // Why isn't HeuristicCommitException used in JTA ?
3248
// And why define something that is not used ?
3249
if (trace)
3250            log.trace("NOT Throwing HeuristicCommitException, tx=" + toString()
3251                      + ", status=" + TxUtils.getStatusAsString(status));
3252         return;
3253      }
3254   }
3255
3256   private void checkHeuristicsButDoNotThrowHeuristicHazard()
3257         throws HeuristicMixedException JavaDoc,
3258                HeuristicRollbackException JavaDoc
3259   {
3260      if (committedResources > 0 && rolledbackResources > 0)
3261         heuristicCode = XAException.XA_HEURMIX;
3262                   
3263      switch (getFullHeuristicCode())
3264      {
3265      case XAException.XA_HEURHAZ:
3266         if (TxManager.getInstance().isReportHeuristicHazardAsHeuristicMixed())
3267         {
3268            if (trace)
3269               log.trace("Throwing HeuristicMixedException instead of " +
3270                         "HeuristicHazardException, tx=" + toString() +
3271                         ", status=" + TxUtils.getStatusAsString(status));
3272            throw new HeuristicMixedException JavaDoc();
3273         }
3274         else
3275         {
3276            if (trace)
3277               log.trace("NOT throwing HeuristicHazardException, tx=" +
3278                         toString() + ", status=" +
3279                         TxUtils.getStatusAsString(status));
3280            return;
3281         }
3282      case XAException.XA_HEURMIX:
3283         if (trace)
3284            log.trace("Throwing HeuristicMixedException, tx=" + toString() +
3285                      ", status=" + TxUtils.getStatusAsString(status));
3286         throw new HeuristicMixedException JavaDoc();
3287      case XAException.XA_HEURRB:
3288         if (trace)
3289            log.trace("Throwing HeuristicRollbackException, tx=" + toString() +
3290                      ", status=" + TxUtils.getStatusAsString(status));
3291         throw new HeuristicRollbackException JavaDoc();
3292      case XAException.XA_HEURCOM:
3293         // Why isn't HeuristicCommitException used in JTA ?
3294
// And why define something that is not used ?
3295
if (trace)
3296            log.trace("NOT Throwing HeuristicCommitException, tx=" + toString()
3297                      + ", status=" + TxUtils.getStatusAsString(status));
3298         return;
3299      }
3300   }
3301  
3302   /**
3303    * Registers this transaction as a resource with its parent coordinator.
3304    * Called from <code>enlistResource</code>.
3305    *
3306    * @throws RollbackException
3307    * @throws java.lang.IllegalStateException
3308    *
3309    * @throws SystemException
3310    */

3311   private void registerResourceWithParentCoordinator()
3312           throws RollbackException JavaDoc
3313   {
3314      if (trace)
3315         log.trace("registerResourceWithParentCoordinator(): Entered, tx=" +
3316                   toString() + ", status=" + TxUtils.getStatusAsString(status));
3317      
3318      Resource JavaDoc r = null;
3319
3320      if (Proxy.isProxyClass(parentCoordinator.getClass()))
3321      {
3322         // DTM coordinator case
3323
if (dtmResourceFactory != null)
3324            r = dtmResourceFactory.createResource(getLocalIdValue());
3325         else
3326         {
3327            log.warn("Missing DTM resource factory, tx=" + toString());
3328            status = Status.STATUS_MARKED_ROLLBACK;
3329            throw new RollbackException JavaDoc("Missing DTM resource factory");
3330         }
3331      }
3332      else
3333      {
3334         // OTS coordinator case
3335
if (otsResourceFactory != null)
3336            r = otsResourceFactory.createResource(getLocalIdValue());
3337         else
3338         {
3339            log.warn("Missing OTS resource factory, tx=" + toString());
3340            status = Status.STATUS_MARKED_ROLLBACK;
3341            throw new RollbackException JavaDoc("Missing OTS resource factory");
3342         }
3343      }
3344
3345      try
3346      {
3347         unlock();
3348         try
3349         {
3350            recoveryCoordinator = parentCoordinator.registerResource(r);
3351            registeredResource = r;
3352         }
3353         finally
3354         {
3355            lock();
3356         }
3357      }
3358      catch (TransactionInactiveException e)
3359      {
3360         status = Status.STATUS_MARKED_ROLLBACK;
3361         if (trace)
3362            log.trace("Got TransactionInactiveException, " +
3363                      "throwing RollbackException");
3364         throw new RollbackException JavaDoc(e.toString());
3365      }
3366      catch (TransactionRolledbackException JavaDoc e)
3367      {
3368         status = Status.STATUS_MARKED_ROLLBACK;
3369         if (trace)
3370            log.trace("Got TransactionRolledbackException, " +
3371                      "throwing RollbackException");
3372         throw new RollbackException JavaDoc(e.toString());
3373      }
3374      catch (RemoteException JavaDoc e)
3375      {
3376         status = Status.STATUS_MARKED_ROLLBACK;
3377         if (trace)
3378            log.trace("Got RemoteException, throwing RollbackException");
3379         throw new RollbackException JavaDoc(e.toString());
3380      }
3381   }
3382
3383   /**
3384    * Prepare all enlisted resources.
3385    * If the first phase of the commit process results in a decision
3386    * to commit the <code>status</code> will be
3387    * <code>Status.STATUS_PREPARED</code> on return.
3388    * Otherwise the <code>status</code> will be
3389    * <code>Status.STATUS_MARKED_ROLLBACK</code> on return.
3390    * This will release the lock while calling out.
3391    *
3392    * @return True iff all resources voted read-only.
3393    */

3394   private boolean prepareResources()
3395   {
3396      boolean readOnly = true;
3397
3398      status = Status.STATUS_PREPARING;
3399      
3400      // Prepare XA resources.
3401

3402      if (trace)
3403         log.trace("Preparing " + xaResources.size() + " XA resource(s)");
3404
3405      for (int i = 0; i < xaResources.size(); ++i)
3406      {
3407         // Abort prepare on state change.
3408
if (status != Status.STATUS_PREPARING)
3409            return false;
3410
3411         EnlistedXAResource resource = (EnlistedXAResource) xaResources.get(i);
3412
3413         if (resource.isResourceManager() == false)
3414            continue; // This RM already prepared.
3415

3416         // Ignore the last resource it is done later
3417
if (resource == lastResource)
3418            continue;
3419
3420         try
3421         {
3422            int vote = resource.prepare();
3423
3424            if (trace)
3425            {
3426               String JavaDoc strVote =
3427                  (vote == RS_VOTE_OK) ?
3428                     "OK" :
3429                     ((vote == RS_VOTE_READONLY) ? "READONLY" : "ROLLBACK");
3430
3431               log.trace("XA resource voted " + strVote);
3432            }
3433
3434            if (vote == RS_VOTE_OK)
3435               readOnly = false;
3436            else if (vote != RS_VOTE_READONLY)
3437            {
3438               // Neither commit vote nor readonly vote: rollback.
3439
if (trace)
3440                  log.trace("prepareResources got a rollback vote " +
3441                            "from an XA resource, tx=" + toString() +
3442                            " resource=" + resource, new Exception JavaDoc());
3443               status = Status.STATUS_MARKED_ROLLBACK;
3444               return false;
3445            }
3446         }
3447         catch (XAException JavaDoc e)
3448         {
3449            readOnly = false;
3450            logXAException(e);
3451            switch (e.errorCode)
3452            {
3453            case XAException.XA_HEURCOM:
3454               // Heuristic commit is not that bad when preparing.
3455
// But it means trouble if we have to rollback.
3456
gotHeuristic(resource, e.errorCode);
3457               break;
3458            case XAException.XA_HEURRB:
3459            case XAException.XA_HEURMIX:
3460            case XAException.XA_HEURHAZ:
3461               // Other heuristic exceptions cause a rollback
3462
gotHeuristic(resource, e.errorCode);
3463               // fall through
3464
default:
3465               cause = e;
3466               status = Status.STATUS_MARKED_ROLLBACK;
3467               break;
3468            }
3469         }
3470         catch (Throwable JavaDoc t)
3471         {
3472            if (t instanceof RecoveryTestingException)
3473               throw (RecoveryTestingException)t;
3474            
3475            if (trace)
3476               log.trace("unhandled throwable in prepareResources " +
3477                         toString(), t);
3478            status = Status.STATUS_MARKED_ROLLBACK;
3479            cause = t;
3480            return false;
3481         }
3482      }
3483      
3484      // Prepare remote DTM/OTS resources.
3485

3486      if (trace)
3487         log.trace("Preparing " + remoteResources.size() +
3488                   " remote resource(s)");
3489
3490      for (int i = 0; i < remoteResources.size(); ++i)
3491      {
3492         // Abort prepare on state change.
3493
if (status != Status.STATUS_PREPARING)
3494            return false;
3495
3496         EnlistedRemoteResource resource =
3497                 (EnlistedRemoteResource) remoteResources.get(i);
3498
3499         try
3500         {
3501            int vote = resource.prepare();
3502
3503            if (trace)
3504            {
3505               String JavaDoc strVote =
3506                  (vote == RS_VOTE_OK) ?
3507                     "OK" :
3508                     ((vote == RS_VOTE_READONLY) ? "READONLY" : "ROLLBACK");
3509
3510               log.trace("Remote resource voted " + strVote);
3511            }
3512
3513            if (vote == RS_VOTE_OK)
3514               readOnly = false;
3515            else if (vote != RS_VOTE_READONLY)
3516            {
3517               // Neither commit vote nor readonly vote: rollback.
3518
if (trace)
3519                  log.trace("prepareResources got a rollback vote " +
3520                            "from a remote resource, tx=" + toString() +
3521                            ", resource=" + resource, new Exception JavaDoc());
3522               status = Status.STATUS_MARKED_ROLLBACK;
3523               return false;
3524            }
3525         }
3526         catch (Exception JavaDoc e)
3527         {
3528            // Blanket catch for the following exception types:
3529
// TransactionAlreadyPreparedException,
3530
// HeuristicMixedException,
3531
// HeuristicHazardException, and
3532
// RemoteException, which includes TransactionRolledbackException.
3533
if (trace)
3534               log.trace("Exception in prepareResources, tx=" + toString(), e);
3535
3536            if (e instanceof HeuristicMixedException JavaDoc)
3537               gotHeuristic(resource, XAException.XA_HEURMIX);
3538            else if (e instanceof HeuristicHazardException)
3539               gotHeuristic(resource, XAException.XA_HEURHAZ);
3540
3541            cause = e;
3542            status = Status.STATUS_MARKED_ROLLBACK;
3543         }
3544      }
3545      
3546      // Abort prepare on state change.
3547
if (status != Status.STATUS_PREPARING)
3548         return false;
3549      
3550      // Are we doing the last resource gambit?
3551
if (lastResource != null)
3552      {
3553         try
3554         {
3555            lastResource.prepareLastResource();
3556            lastResource.commit(false);
3557         }
3558         catch (XAException JavaDoc e)
3559         {
3560            if (trace)
3561               log.trace("prepareResources got XAException" +
3562                         " when committing last resource, tx=" + toString(), e);
3563            logXAException(e);
3564            switch (e.errorCode)
3565            {
3566               // No need to handle the case of XAException.XA_HEURCOM,
3567
// which is swallowed by EnlistedXAResource.commit()
3568
case XAException.XA_HEURRB:
3569            case XAException.XA_HEURMIX:
3570            case XAException.XA_HEURHAZ:
3571               //usually throws an exception, but not for a couple of cases.
3572
gotHeuristic(lastResource, e.errorCode);
3573               // fall through
3574
default:
3575               // take the XAException as a "no" vote from the last resource
3576
status = Status.STATUS_MARKED_ROLLBACK;
3577               cause = e;
3578               return false; // to make the caller look at
3579
// at the "marked rollback" status
3580
}
3581         }
3582         catch (Throwable JavaDoc t)
3583         {
3584            if (trace)
3585               log.trace("unhandled throwable in prepareResources, tx=" +
3586                         toString(), t);
3587            status = Status.STATUS_MARKED_ROLLBACK;
3588            cause = t;
3589            return false; // to make the caller look at
3590
// the "marked rollback" status
3591
}
3592      }
3593
3594      if (status == Status.STATUS_PREPARING)
3595         status = Status.STATUS_PREPARED;
3596      else
3597         return false;
3598
3599      return readOnly;
3600   }
3601
3602   /**
3603    * Commit all enlisted resources.
3604    * This will release the lock while calling out.
3605    */

3606   private void commitResources(boolean onePhase)
3607   {
3608      status = Status.STATUS_COMMITTING;
3609      doCommitResources(onePhase);
3610      if (xaResourcesToRetry > 0 || remoteResourcesToRetry > 0)
3611      {
3612         int retryLimit = TxManager.getInstance().getCompletionRetryLimit();
3613         for (int n = 0;
3614              n < retryLimit &&
3615                 (xaResourcesToRetry > 0 || remoteResourcesToRetry > 0);
3616              n++)
3617         {
3618            sleep(TxManager.getInstance().getCompletionRetryTimeoutMillis());
3619            doCommitResources(onePhase);
3620         }
3621      }
3622      checkCommitCompletion();
3623   }
3624
3625   /**
3626    * Do the actual resource commiting.
3627    */

3628   private void doCommitResources(boolean onePhase)
3629   {
3630      xaResourcesToRetry = 0;
3631      remoteResourcesToRetry = 0;
3632      
3633      // Commit XA resources.
3634
for (int i = 0; i < xaResources.size(); ++i)
3635      {
3636
3637         // Abort commit on state change.
3638
if (status != Status.STATUS_COMMITTING)
3639            return;
3640
3641         EnlistedXAResource resource =
3642                 (EnlistedXAResource) xaResources.get(i);
3643
3644         // Ignore the last resource, it is already committed
3645
if (onePhase == false && lastResource == resource)
3646            continue;
3647
3648         try
3649         {
3650            resource.commit(onePhase);
3651         }
3652         catch (XAException JavaDoc e)
3653         {
3654            logXAException(e);
3655            switch (e.errorCode)
3656            {
3657               // No need to handle the case of XAException.XA_HEURCOM,
3658
// which is swallowed by EnlistedXAResource.commit()
3659
case XAException.XA_HEURRB:
3660            case XAException.XA_HEURMIX:
3661            case XAException.XA_HEURHAZ:
3662               //usually throws an exception, but not for a couple of cases.
3663
gotHeuristic(resource, e.errorCode);
3664               if (onePhase)
3665                  status = Status.STATUS_MARKED_ROLLBACK;
3666               break;
3667            case XAException.XAER_RMERR:
3668               // Not much we can do if there is an RMERR in the
3669
// commit phase of 2pc. I guess we take it as a heuristic
3670
// rollback and try the other RMs.
3671
gotHeuristic(null, XAException.XA_HEURRB);
3672               if (onePhase)
3673                  status = Status.STATUS_MARKED_ROLLBACK;
3674               break;
3675            case XAException.XAER_RMFAIL:
3676            case XAException.XA_RETRY:
3677               if (onePhase)
3678                  status = Status.STATUS_MARKED_ROLLBACK;
3679               else
3680                  // The XAResource remains prepared. Retry the commit later.
3681
xaResourcesToRetry++;
3682               break;
3683            case XAException.XAER_NOTA:
3684            case XAException.XAER_INVAL:
3685            case XAException.XAER_PROTO:
3686            default:
3687               // This should never happen!
3688
cause = e;
3689               if (onePhase)
3690                  status = Status.STATUS_MARKED_ROLLBACK;
3691               else
3692                  log.warn("Could not recover from unexpected XAException: " +
3693                           "tx=" + toString() + " errorCode=" +
3694                           TxUtils.getXAErrorCodeAsString(e.errorCode), e);
3695               break;
3696            }
3697         }
3698         catch (Throwable JavaDoc t)
3699         {
3700            if (t instanceof RecoveryTestingException)
3701               throw (RecoveryTestingException) t;
3702            if (trace)
3703               log.trace("Unhandled throwable in doCommitResources " +
3704                         toString(), t);
3705         }
3706      }
3707
3708      // Commit remote DTM/OTS resources.
3709
for (int i = 0; i < remoteResources.size(); ++i)
3710      {
3711         // Abort commit on state change.
3712
if (status != Status.STATUS_COMMITTING)
3713            return;
3714
3715         EnlistedRemoteResource resource =
3716                 (EnlistedRemoteResource) remoteResources.get(i);
3717
3718         try
3719         {
3720            resource.commit(onePhase);
3721         }
3722         catch (TransactionRolledbackException JavaDoc e)
3723         {
3724            if (trace)
3725               log.trace("Exception in doCommitResources, tx=" + toString(), e);
3726
3727            if (onePhase)
3728               status = Status.STATUS_MARKED_ROLLBACK;
3729            else
3730            {
3731               // The resource decided to rollback in the second phase of 2PC:
3732
// this is a heuristic outcome.
3733
gotHeuristic(null, XAException.XA_HEURRB);
3734            }
3735         }
3736         catch (HeuristicRollbackException JavaDoc e)
3737         {
3738            if (trace)
3739               log.trace("Exception in doCommitResources, tx=" + toString(), e);
3740
3741            gotHeuristic(resource, XAException.XA_HEURRB);
3742            if (onePhase)
3743               status = Status.STATUS_MARKED_ROLLBACK;
3744         }
3745         catch (HeuristicMixedException JavaDoc e)
3746         {
3747            if (trace)
3748               log.trace("Exception in doCommitResources, tx=" + toString(), e);
3749
3750            gotHeuristic(resource, XAException.XA_HEURMIX);
3751            if (onePhase)
3752               status = Status.STATUS_MARKED_ROLLBACK;
3753         }
3754         catch (HeuristicHazardException e)
3755         {
3756            if (trace)
3757               log.trace("Exception in doCommitResources, tx=" + toString(), e);
3758
3759            gotHeuristic(resource, XAException.XA_HEURHAZ);
3760            if (onePhase)
3761               status = Status.STATUS_MARKED_ROLLBACK;
3762         }
3763         catch (TransactionNotPreparedException e)
3764         {
3765            // This should never happen!
3766
cause = e;
3767            if (trace)
3768               log.trace("Exception in doCommitResources, tx=" + toString(), e);
3769            if (onePhase)
3770               status = Status.STATUS_MARKED_ROLLBACK;
3771            else
3772               log.warn("Could not recover from unexpected exception in " +
3773                        "doCommitResources: tx=" + toString() +
3774                        " exception=" + e);
3775         }
3776         catch (NoSuchObjectException JavaDoc e)
3777         {
3778            if (trace)
3779               log.trace("Exception in doCommitResources, tx=" + toString(), e);
3780            if (onePhase)
3781               status = Status.STATUS_MARKED_ROLLBACK;
3782            else
3783            {
3784               // Do nothing -- we must be doing crash recovery and the
3785
// remote resource has been committed before the crash.
3786
log.warn("Ignoring NoSuchObjectException in doCommitResources: "
3787                        + " tx=" + toString() + " exception=" + e);
3788            }
3789         }
3790         catch (RemoteException JavaDoc e)
3791         {
3792            if (trace)
3793               log.trace("Exception in doCommitResources, tx=" + toString(), e);
3794            if (onePhase)
3795               status = Status.STATUS_MARKED_ROLLBACK;
3796            else
3797            {
3798               // The remote resource remains prepared. Retry the commit later.
3799
remoteResourcesToRetry++;
3800            }
3801         }
3802      }
3803   }
3804   
3805   /**
3806    * Create retry timeout for calling retryCommitXAResources().
3807    */

3808   private void commitXAResourcesAfterTimeout()
3809   {
3810      xaRetryTimeout = timeoutFactory.createTimeout(
3811         System.currentTimeMillis() +
3812               TxManager.getInstance().getXARetryTimeoutMillis(),
3813         new TimeoutTarget()
3814         {
3815            public void timedOut(Timeout timeout)
3816            {
3817               if (trace)
3818                  log.trace("XA retry timeout expired for tx=" +
3819                            TransactionImpl.this.toString() +
3820                            ", retrying commit on XAResources");
3821               lock();
3822               try
3823               {
3824                  retryCommitXAResources();
3825                  if (xaResourcesToRetry > 0)
3826                  {
3827                     xaRetryTimeout = timeoutFactory.createTimeout(
3828                        System.currentTimeMillis() +
3829                              TxManager.getInstance().getXARetryTimeoutMillis(),
3830                        this);
3831                  }
3832                  else if (remoteResourcesToRetry == 0 &&
3833                        heuristicCode == HEUR_NONE)
3834                  {
3835                     completeTransaction();
3836                  }
3837               }
3838               finally
3839               {
3840                  unlock();
3841               }
3842            }
3843         });
3844   }
3845
3846   // Pre-condition: this TransactionImpl instance is locked.
3847
// Post-condition: this TransactionImpl instance remains locked.
3848
private void retryCommitXAResources()
3849   {
3850      if (trace)
3851         log.trace("Retrying commit XA resources, tx=" + toString() +
3852                   ", status=" + TxUtils.getStatusAsString(status) +
3853                   ", xaResourcesToRetry=" + xaResourcesToRetry);
3854
3855      xaResourcesToRetry = 0;
3856        
3857      for (int i = 0; i < xaResources.size(); ++i)
3858      {
3859         EnlistedXAResource resource =
3860                 (EnlistedXAResource) xaResources.get(i);
3861
3862         // Ignore the last resource, it is already committed
3863
if (lastResource == resource)
3864            continue;
3865
3866         try
3867         {
3868            resource.commit(false);
3869         }
3870         catch (XAException JavaDoc e)
3871         {
3872            logXAException(e);
3873            switch (e.errorCode)
3874            {
3875               // No need to handle the case of XAException.XA_HEURCOM,
3876
// which is swallowed by EnlistedXAResource.commit()
3877
case XAException.XA_HEURRB:
3878            case XAException.XA_HEURMIX:
3879            case XAException.XA_HEURHAZ:
3880               //usually throws an exception, but not for a couple of cases.
3881
gotHeuristic(resource, e.errorCode);
3882               break;
3883            case XAException.XAER_RMERR:
3884               // Not much we can do if there is an RMERR in the
3885
// commit phase of 2pc. I guess we take it as a heuristic
3886
// rollback and try the other RMs.
3887
gotHeuristic(null, XAException.XA_HEURRB);
3888               break;
3889            case XAException.XAER_RMFAIL:
3890            case XAException.XA_RETRY:
3891               // The XAResource remains prepared. Retry the commit later.
3892
xaResourcesToRetry++;
3893               break;
3894            case XAException.XAER_NOTA:
3895            case XAException.XAER_INVAL:
3896            case XAException.XAER_PROTO:
3897            default:
3898               // This should never happen!
3899
log.warn("Could not recover from unexpected XAException: " +
3900                        "tx=" + toString() + " errorCode=" +
3901                         TxUtils.getXAErrorCodeAsString(e.errorCode), e);
3902               break;
3903            }
3904         }
3905         catch (Throwable JavaDoc t)
3906         {
3907            if (t instanceof RecoveryTestingException)
3908               throw (RecoveryTestingException) t;
3909            if (trace)
3910               log.trace("unhandled throwable in retryCommitXAResources " +
3911                         this, t);
3912         }
3913      }
3914      
3915      if (trace)
3916         log.trace("Finished retrying commit XA resources, tx=" + toString() +
3917                   ", status=" + TxUtils.getStatusAsString(status) +
3918                   ", xaResourcesToRetry=" + xaResourcesToRetry);
3919
3920      checkCommitCompletion();
3921   }
3922   
3923   // Pre-condition: this TransactionImpl instance is locked.
3924
// Post-condition: this TransactionImpl instance remains locked.
3925
private void retryCommitRemoteResources()
3926   {
3927      if (trace)
3928         log.trace("Retrying commit remote resources, tx=" + toString() +
3929                   ", status=" + TxUtils.getStatusAsString(status) +
3930                   ", " + remoteResources.size() + " remote resources");
3931
3932      remoteResourcesToRetry = 0;
3933      
3934      // Commit remote DTM/OTS resources.
3935
for (int i = 0; i < remoteResources.size(); ++i)
3936      {
3937         EnlistedRemoteResource resource =
3938                 (EnlistedRemoteResource) remoteResources.get(i);
3939
3940         try
3941         {
3942            resource.commit(false);
3943         }
3944         catch (HeuristicRollbackException JavaDoc e)
3945         {
3946            if (trace)
3947               log.trace("Exception in retryCommitRemoteResources, tx=" +
3948                         toString(), e);
3949
3950            gotHeuristic(resource, XAException.XA_HEURRB);
3951         }
3952         catch (HeuristicMixedException JavaDoc e)
3953         {
3954            if (trace)
3955               log.trace("Exception in retryCommitRemoteResources, tx=" +
3956                         toString(), e);
3957
3958            gotHeuristic(resource, XAException.XA_HEURMIX);
3959         }
3960         catch (HeuristicHazardException e)
3961         {
3962            if (trace)
3963               log.trace("Exception in retryCommitRemoteResources, tx=" +
3964                         toString(), e);
3965
3966            gotHeuristic(resource, XAException.XA_HEURHAZ);
3967         }
3968         catch (TransactionNotPreparedException e)
3969         {
3970            // This should never happen!
3971
cause = e;
3972            if (trace)
3973               log.trace("Exception in retryCommitRemoteResources, tx=" +
3974                         toString(), e);
3975            log.warn("Could not recover from unexpected exception in " +
3976                     "retryCommitRemoteResources: tx=" + toString() +
3977                     " exception=" + e);
3978         }
3979         catch (NoSuchObjectException JavaDoc e)
3980         {
3981            if (trace)
3982               log.trace("Exception in retryCommitRemoteResources, tx=" +
3983                         toString(), e);
3984            
3985            // Do nothing -- we must be doing crash recovery and the
3986
// remote resource has been committed before the crash.
3987
log.warn("Ignoring NoSuchObjectException in " +
3988                     "retryCommitRemoteResources: tx=" + toString() +
3989                     " exception=" + e);
3990         }
3991         catch (RemoteException JavaDoc e)
3992         {
3993            if (trace)
3994               log.trace("Exception in retryCommitRemoteResources, tx=" +
3995                         toString(), e);
3996            
3997            // The remote resource remains prepared. Retry the commit later.
3998
remoteResourcesToRetry++;
3999         }
4000      }
4001
4002      if (trace)
4003         log.trace("Finished retrying commit remote resources, tx=" + toString()
4004                   + ", status=" + TxUtils.getStatusAsString(status) +
4005                   ", remoteResourcesToRetry=" + remoteResourcesToRetry +
4006                   " of " + remoteResources.size() + " remote resources");
4007
4008      checkCommitCompletion();
4009   }
4010
4011   private void checkCommitCompletion()
4012   {
4013      if (status != Status.STATUS_COMMITTING)
4014         return;
4015      
4016      consolidateHeuristics();
4017
4018      if (xaResourcesToRetry == 0 && remoteResourcesToRetry == 0)
4019      {
4020         status = Status.STATUS_COMMITTED;
4021         if (heuristicHazard || heuristicCode != HEUR_NONE)
4022         {
4023            heuristicHazard = false;
4024            
4025            // Save the heuristic status to stable storage. Do not include
4026
// a locally-detected heuristic hazard, as the actual heuristic
4027
// outcome is known at this point.
4028
RecoveryLogger logger = TxManager.getInstance().getRecoveryLogger();
4029            if (logger != null)
4030               logger.saveHeuristicStatus(xid.getLocalIdValue(),
4031                                          foreignTx,
4032                                          xid.getFormatId(),
4033                                          xid.getGlobalTransactionId(),
4034                                          inboundBranchQualifier,
4035                                          status,
4036                                          heuristicCode,
4037                                          heuristicHazard,
4038                                          getXAResourceHeuristics(),
4039                                          getRemoteResourceHeuristics());
4040            if (!foreignTx &&
4041                  !TxManager.getInstance().isRootBranchRemembersHeuristicDecisions() &&
4042                  forgetResources())
4043            {
4044               // Clear heuristic status
4045
if (logger != null)
4046               {
4047                  long localId = xid.getLocalIdValue();
4048                  logger.clearHeuristicStatus(localId);
4049                  if (completionHandler != null)
4050                     completionHandler.handleTxCompletion(localId);
4051               }
4052            }
4053         }
4054         else if (completionHandler != null)
4055            completionHandler.handleTxCompletion(xid.getLocalIdValue());
4056      }
4057      else if (!heuristicHazard)
4058      {
4059         heuristicHazard = true;
4060
4061         // Save the heuristic status to stable storage, including the
4062
// locally-detected heuristic hazard.
4063
RecoveryLogger logger = TxManager.getInstance().getRecoveryLogger();
4064         if (logger != null)
4065            logger.saveHeuristicStatus(xid.getLocalIdValue(),
4066                                       foreignTx,
4067                                       xid.getFormatId(),
4068                                       xid.getGlobalTransactionId(),
4069                                       inboundBranchQualifier,
4070                                       status,
4071                                       heuristicCode,
4072                                       heuristicHazard,
4073                                       getXAResourceHeuristics(),
4074                                       getRemoteResourceHeuristics());
4075         
4076         if (!foreignTx &&
4077               !TxManager.getInstance().isRootBranchRemembersHeuristicDecisions())
4078            forgetResources();
4079      }
4080   }
4081   
4082   /**
4083    * Rollback all enlisted resources and complete the transaction.
4084    * This will release the lock while calling out.
4085    */

4086   private void rollbackResourcesAndCompleteTransaction()
4087   {
4088      status = Status.STATUS_ROLLING_BACK;
4089      doRollbackResources();
4090      if (xaResourcesToRetry > 0 || remoteResourcesToRetry > 0)
4091      {
4092         int retryLimit = TxManager.getInstance().getCompletionRetryLimit();
4093         for (int n = 0;
4094              n < retryLimit &&
4095                 (xaResourcesToRetry > 0 || remoteResourcesToRetry > 0);
4096              n++)
4097         {
4098            sleep(TxManager.getInstance().getCompletionRetryTimeoutMillis());
4099            doRollbackResources();
4100         }
4101      }
4102      
4103      if (xaResourcesToRetry == 0 && remoteResourcesToRetry == 0)
4104      {
4105         status = Status.STATUS_ROLLEDBACK;
4106         
4107         if (consolidateHeuristics() != HEUR_NONE || heuristicHazard)
4108         {
4109            heuristicHazard = false;
4110
4111            // Save the heuristic status to stable storage. Do not include
4112
// a locally-generated heuristic hazard, as the actual heuristic
4113
// outcome is known at this point.
4114
RecoveryLogger logger = TxManager.getInstance().getRecoveryLogger();
4115            if (logger != null)
4116               logger.saveHeuristicStatus(xid.getLocalIdValue(),
4117                                          foreignTx,
4118                                          xid.getFormatId(),
4119                                          xid.getGlobalTransactionId(),
4120                                          inboundBranchQualifier,
4121                                          status,
4122                                          heuristicCode,
4123                                          heuristicHazard,
4124                                          getXAResourceHeuristics(),
4125                                          getRemoteResourceHeuristics());
4126            if (!foreignTx &&
4127                  !TxManager.getInstance().isRootBranchRemembersHeuristicDecisions() &&
4128                  forgetResources())
4129            {
4130               // Clear heuristic status
4131
if (logger != null)
4132               {
4133                  long localId = xid.getLocalIdValue();
4134                  logger.clearHeuristicStatus(localId);
4135               }
4136            }
4137         }
4138         else
4139            completeTransaction();
4140      }
4141      else
4142      {
4143         if (!heuristicHazard)
4144         {
4145            heuristicHazard = true;
4146            consolidateHeuristics();
4147                  
4148            // Save the heuristic status to stable storage, including the
4149
// locally-detected heuristic hazard.
4150
RecoveryLogger logger = TxManager.getInstance().getRecoveryLogger();
4151            if (logger != null)
4152               logger.saveHeuristicStatus(xid.getLocalIdValue(),
4153                                          foreignTx,
4154                                          xid.getFormatId(),
4155                                          xid.getGlobalTransactionId(),
4156                                          inboundBranchQualifier,
4157                                          status,
4158                                          heuristicCode,
4159                                          heuristicHazard,
4160                                          getXAResourceHeuristics(),
4161                                          getRemoteResourceHeuristics());
4162         }
4163         if (xaResourcesToRetry > 0)
4164            rollbackXAResourcesAfterTimeout();
4165      }
4166   }
4167   
4168   /**
4169    * Do the actual resource rolling back.
4170    */

4171   private void doRollbackResources()
4172   {
4173      xaResourcesToRetry = 0;
4174      remoteResourcesToRetry = 0;
4175
4176      // Rollback XA resources.
4177
for (int i = 0; i < xaResources.size(); ++i)
4178      {
4179         EnlistedXAResource resource = (EnlistedXAResource) xaResources.get(i);
4180         try
4181         {
4182            resource.rollback();
4183         }
4184         catch (XAException JavaDoc e)
4185         {
4186            logXAException(e);
4187            switch (e.errorCode)
4188            {
4189               // No need to handle the case of XAException.XA_HEURRB,
4190
// which is swallowed by EnlistedXAResource.rollback()
4191
case XAException.XA_HEURCOM:
4192            case XAException.XA_HEURMIX:
4193            case XAException.XA_HEURHAZ:
4194               gotHeuristic(resource, e.errorCode);
4195               continue;
4196            default:
4197               cause = e;
4198               break;
4199            }
4200         }
4201         catch (Throwable JavaDoc t)
4202         {
4203            if (t instanceof RecoveryTestingException)
4204               throw (RecoveryTestingException) t;
4205            if (trace)
4206               log.trace("unhandled throwable in doRollbackResources " + this,
4207                         t);
4208         }
4209         if (resource.getState() == RS_VOTE_OK)
4210            xaResourcesToRetry++;
4211      }
4212
4213      // Rollback remote DTM/OTS resources.
4214
for (int i = 0; i < remoteResources.size(); ++i)
4215      {
4216         EnlistedRemoteResource resource =
4217                 (EnlistedRemoteResource) remoteResources.get(i);
4218
4219         try
4220         {
4221            resource.rollback();
4222         }
4223         catch (HeuristicCommitException JavaDoc e)
4224         {
4225            gotHeuristic(resource, XAException.XA_HEURCOM);
4226            continue;
4227         }
4228         catch (HeuristicMixedException JavaDoc e)
4229         {
4230            gotHeuristic(resource, XAException.XA_HEURMIX);
4231            continue;
4232         }
4233         catch (HeuristicHazardException e)
4234         {
4235            gotHeuristic(resource, XAException.XA_HEURHAZ);
4236            continue;
4237         }
4238         catch (RemoteException JavaDoc e)
4239         {
4240            // No need to do much here. If the resource didn't get the
4241
// rollback then it will eventually call replay completion on
4242
// the recovery coordinator. We increase remoteResourcesToRetry
4243
// for heuristic hazard reporting purposes only.
4244
remoteResourcesToRetry++;
4245            if (trace)
4246               log.trace("Ignoring exception in remote resource rollback, tx=" +
4247                         toString(), e);
4248         }
4249      }
4250   }
4251   
4252   /**
4253    * Create timeout for calling retryRollbackXAResources().
4254    */

4255   private void rollbackXAResourcesAfterTimeout()
4256   {
4257      xaRetryTimeout = timeoutFactory.createTimeout(
4258         System.currentTimeMillis() +
4259               TxManager.getInstance().getXARetryTimeoutMillis(),
4260         new TimeoutTarget()
4261         {
4262            public void timedOut(Timeout timeout)
4263            {
4264               if (trace)
4265                  log.trace("XA retry timeout expired for tx=" +
4266                            TransactionImpl.this.toString() +
4267                            ", retrying rollback on XAResources");
4268               lock();
4269               try
4270               {
4271                  retryRollbackXAResources();
4272                  if (xaResourcesToRetry > 0)
4273                  {
4274                     xaRetryTimeout = timeoutFactory.createTimeout(
4275                        System.currentTimeMillis() +
4276                              TxManager.getInstance().getXARetryTimeoutMillis(),
4277                        this);
4278                  }
4279                  else if (remoteResourcesToRetry == 0 &&
4280                        heuristicCode == HEUR_NONE)
4281                  {
4282                     completeTransaction();
4283                  }
4284               }
4285               finally
4286               {
4287                  unlock();
4288               }
4289            }
4290         });
4291   }
4292      
4293   /**
4294    * Cancel XA retry timeout.
4295    */

4296   private void cancelXARetryTimeout()
4297   {
4298      if (xaRetryTimeout != null)
4299      {
4300         Timeout rt = xaRetryTimeout;
4301         xaRetryTimeout = null;
4302         
4303         unlock();
4304         try
4305         {
4306            rt.cancel();
4307         }
4308         catch (Exception JavaDoc e)
4309         {
4310            if (trace)
4311               log.trace("failed to cancel XA retry timeout, tx=" + toString(),
4312                         e);
4313         }
4314         finally
4315         {
4316            lock();
4317         }
4318      }
4319   }
4320   
4321   private int[] getXAResourceHeuristics()
4322   {
4323      if (xaResourcesWithHeuristicDecisions == null)
4324         return null;
4325      else
4326      {
4327         int[] heurCodes = new int[xaResourcesWithHeuristicDecisions.size()];
4328         for (int i = 0; i < xaResourcesWithHeuristicDecisions.size(); ++i)
4329         {
4330            EnlistedResource resource =
4331               (EnlistedResource) xaResourcesWithHeuristicDecisions.get(i);
4332            heurCodes[i] = resource.getHeuristicCode();
4333         }
4334         return heurCodes;
4335      }
4336   }
4337   
4338   private HeuristicStatus[] getRemoteResourceHeuristics()
4339   {
4340      if (remoteResourcesWithHeuristicDecisions == null)
4341         return null;
4342      else
4343      {
4344         HeuristicStatus s[] =
4345            new HeuristicStatus[remoteResourcesWithHeuristicDecisions.size()];
4346         for (int i = 0; i < remoteResourcesWithHeuristicDecisions.size(); ++i)
4347         {
4348            EnlistedRemoteResource resource =
4349               (EnlistedRemoteResource) remoteResourcesWithHeuristicDecisions.get(i);
4350            s[i] = new HeuristicStatus(
4351                  resource.getHeuristicCode(),
4352                  resourceToString(resource.getRemoteResource()));
4353         }
4354         return s;
4355      }
4356   }
4357   
4358   
4359   private void retryRollbackXAResources()
4360   {
4361      xaResourcesToRetry = 0;
4362
4363      // Rollback XA resources.
4364
for (int i = 0; i < xaResources.size(); ++i)
4365      {
4366         EnlistedXAResource resource = (EnlistedXAResource) xaResources.get(i);
4367         try
4368         {
4369            resource.rollback();
4370         }
4371         catch (XAException JavaDoc e)
4372         {
4373            logXAException(e);
4374            switch (e.errorCode)
4375            {
4376               // No need to handle the case of XAException.XA_HEURRB,
4377
// which is swallowed by EnlistedXAResource.rollback()
4378
case XAException.XA_HEURCOM:
4379            case XAException.XA_HEURMIX:
4380            case XAException.XA_HEURHAZ:
4381               gotHeuristic(resource, e.errorCode);
4382               continue;
4383            default:
4384               cause = e;
4385               break;
4386            }
4387         }
4388         catch (Throwable JavaDoc t)
4389         {
4390            if (t instanceof RecoveryTestingException)
4391               throw (RecoveryTestingException) t;
4392            if (trace)
4393               log.trace("unhandled throwable in retryRollbackXAResources " +
4394                         this, t);
4395         }
4396         if (resource.getState() == RS_VOTE_OK)
4397            xaResourcesToRetry++;
4398      }
4399
4400      if (xaResourcesToRetry == 0 && remoteResourcesToRetry == 0)
4401         status = Status.STATUS_ROLLEDBACK;
4402   }
4403   
4404   private boolean forgetResources()
4405   {
4406      boolean success = true;
4407      if (xaResourcesWithHeuristicDecisions != null)
4408      {
4409         for (int i = 0; i < xaResourcesWithHeuristicDecisions.size(); ++i)
4410         {
4411            EnlistedResource resource =
4412               (EnlistedResource) xaResourcesWithHeuristicDecisions.get(i);
4413            resource.forget();
4414            if (resource.getState() != RS_FORGOT)
4415               success = false;
4416         }
4417      }
4418
4419      if (remoteResourcesWithHeuristicDecisions != null)
4420      {
4421         for (int i = 0; i < remoteResourcesWithHeuristicDecisions.size(); ++i)
4422         {
4423            EnlistedResource resource =
4424               (EnlistedResource) remoteResourcesWithHeuristicDecisions.get(i);
4425            resource.forget();
4426            if (resource.getState() != RS_FORGOT)
4427               success = false;
4428         }
4429      }
4430      return success;
4431   }
4432   
4433   /**
4434    * Create an Xid representing a new branch of this transaction.
4435    */

4436   private Xid JavaDoc createXidBranch()
4437   {
4438      long branchId = ++lastBranchId;
4439
4440      return xidFactory.newBranch(xid, branchId);
4441   }
4442
4443   /**
4444    * Determine the commit strategy
4445    *
4446    * @return 0 for nothing to do, 1 for one phase and 2 for two phase
4447    */

4448   private int getCommitStrategy()
4449   {
4450      int xaResourceCount = xaResources.size();
4451      int remoteResourceCount = remoteResources.size();
4452      int resourceCount = xaResourceCount + remoteResourceCount;
4453
4454      if (resourceCount == 0)
4455         return 0;
4456
4457      if (resourceCount == 1)
4458         return 1;
4459
4460      // At least two resources have participated in this transaction.
4461

4462      if (remoteResourceCount > 0)
4463      {
4464         // They cannot be all XA resources on the same RM,
4465
// as at least one of them is a remote DTM/OTS resource.
4466
return 2;
4467      }
4468      else // They are all XA resources, look if the're all on the same RM.
4469
{
4470         // First XAResource surely isResourceManager, it's the first!
4471
for (int i = 1; i < xaResourceCount; ++i)
4472         {
4473            EnlistedXAResource resource = (EnlistedXAResource) xaResources.get(i);
4474            if (resource.isResourceManager())
4475            {
4476               // this one is not the same rm as previous ones,
4477
// there must be at least 2
4478
return 2;
4479            }
4480
4481         }
4482         // All RMs are the same one, one phase commit is ok.
4483
return 1;
4484      }
4485   }
4486
4487   /**
4488   * Gets stringfied references for all enlisted resources that voted commit.
4489   *
4490   * @return an array of stringfied <code>Resource</code> references.
4491   */

4492   private String JavaDoc[] getStringfiedRemoteResourcesThatVotedCommit()
4493   {
4494      List JavaDoc resourcesThatVotedCommit = new ArrayList JavaDoc(remoteResources.size());
4495      
4496      for (int i = 0; i < remoteResources.size(); ++i)
4497      {
4498         EnlistedRemoteResource enlistedRemoteResource =
4499                 (EnlistedRemoteResource) remoteResources.get(i);
4500         if (enlistedRemoteResource.getState() == RS_VOTE_OK)
4501         {
4502            Resource JavaDoc r = enlistedRemoteResource.getRemoteResource();
4503            resourcesThatVotedCommit.add(resourceToString(r));
4504         }
4505      }
4506      return (String JavaDoc[]) resourcesThatVotedCommit.toArray(
4507                              new String JavaDoc[resourcesThatVotedCommit.size()]);
4508   }
4509
4510   /**
4511    * Check we have no outstanding work
4512    *
4513    * @throws IllegalStateException when there is still work
4514    */

4515   private void checkWork()
4516   {
4517      if (work != null)
4518         throw new IllegalStateException JavaDoc("Work still outstanding: " + work +
4519                                         ", tx=" + toString());
4520   }
4521
4522   /**
4523    * Waits (blocking the caller) for the specified number of milliseconds.
4524    *
4525    * @param millis the length of time to sleep in milliseconds.
4526    */

4527   private void sleep(long millis)
4528   {
4529      try
4530      {
4531         unlock();
4532         Thread.sleep(millis);
4533      }
4534      catch (InterruptedException JavaDoc e)
4535      {
4536         // ignore
4537
}
4538      finally
4539      {
4540         lock();
4541      }
4542   }
4543   
4544   // Inner classes -------------------------------------------------
4545

4546   /**
4547    * Represents a resource enlisted in the transaction.
4548    * The resource can be either an XA resource or a remote DTM/OTS resource.
4549    */

4550   private interface EnlistedResource
4551   {
4552      public int getState();
4553      public void remember(int heuristicCode);
4554      int getHeuristicCode();
4555      public void forget();
4556   }
4557
4558   /**
4559    * Represents an XA resource enlisted in the transaction
4560    */

4561   private class EnlistedXAResource
4562           implements EnlistedResource
4563   {
4564      /**
4565       * The XAResource
4566       */

4567      private XAResource JavaDoc xaResource;
4568
4569      /**
4570       * The state of the resources
4571       */

4572      private int resourceState;
4573
4574      /**
4575       * The related XA resource from the same resource manager
4576       */

4577      private EnlistedXAResource resourceSameRM;
4578
4579      /**
4580       * The Xid of this resource
4581       */

4582      private Xid JavaDoc resourceXid;
4583      
4584      /**
4585       * The heuristic status of this resource
4586       */

4587      private int heurCode;
4588
4589      /**
4590       * The XAResourceAccess instance to be released after this
4591       * EnlistedXAResource is committed or rolled back, or null if no
4592       * XAResourceAccess instance needs to be released.
4593       */

4594      private XAResourceAccess xaResourceAccess = null;
4595
4596      /**
4597       * Create a new resource
4598       */

4599      public EnlistedXAResource(XAResource JavaDoc xaResource,
4600                                Xid JavaDoc resourceXid,
4601                                EnlistedXAResource resourceSameRM)
4602      {
4603         this.xaResource = xaResource;
4604         this.resourceXid = resourceXid;
4605         this.resourceSameRM = resourceSameRM;
4606         resourceState = RS_NEW;
4607         heurCode = HEUR_NONE;
4608      }
4609
4610      /**
4611       * Creates a prepared resource.
4612       * This constructor is intended to be used during recovery only.
4613       */

4614      public EnlistedXAResource(XAWork xaWork)
4615      {
4616         this.xaResource = xaWork.res;
4617         this.resourceXid = xaWork.xid;
4618         this.xaResourceAccess = xaWork.xaResourceAccess;
4619         this.resourceSameRM = null;
4620         resourceState = RS_VOTE_OK;
4621         heurCode = HEUR_NONE;
4622      }
4623
4624      /**
4625       * Creates a resource in a heuristically completed state.
4626       * This constructor is intended to be used during recovery only.
4627       */

4628      public EnlistedXAResource(XAResource JavaDoc xaResource,
4629                                Xid JavaDoc resourceXid,
4630                                boolean committed)
4631      {
4632         this.xaResource = xaResource;
4633         this.resourceXid = resourceXid;
4634         this.resourceSameRM = null;
4635         this.resourceState = (committed) ? RS_COMMITTED: RS_ROLLEDBACK;
4636         heurCode = HEUR_UNKNOWN;
4637      }
4638
4639      /**
4640       * Get the XAResource for this resource
4641       */

4642      public XAResource JavaDoc getXAResource()
4643      {
4644         return xaResource;
4645      }
4646
4647      /**
4648       * Get the Xid for this resource
4649       */

4650      public Xid JavaDoc getXid()
4651      {
4652         return resourceXid;
4653      }
4654      
4655      /**
4656       * Gets the state of the XAResource represented by this
4657       * <code>EnlistedXAResource</code> instance.
4658       */

4659      public int getState()
4660      {
4661         return resourceState;
4662      }
4663
4664      /**
4665       * Is the resource enlisted?
4666       */

4667      public boolean isEnlisted()
4668      {
4669         return resourceState == RS_ENLISTED;
4670      }
4671
4672      /**
4673       * Is this a resource manager
4674       */

4675      public boolean isResourceManager()
4676      {
4677         return resourceSameRM == null;
4678      }
4679
4680      /**
4681       * Is this the resource manager for the passed XA resource
4682       */

4683      public boolean isResourceManager(XAResource JavaDoc xaRes)
4684              throws XAException JavaDoc
4685      {
4686         return resourceSameRM == null && xaRes.isSameRM(xaResource);
4687      }
4688
4689      /**
4690       * Is the resource delisted and the XAResource always returns false
4691       * for isSameRM
4692       */

4693      public boolean isDelisted(XAResource JavaDoc xaRes)
4694              throws XAException JavaDoc
4695      {
4696         return resourceState == RS_ENDED && xaResource.isSameRM(xaRes) == false;
4697      }
4698
4699      /**
4700       * Call <code>start()</code> on a XAResource and update
4701       * internal state information.
4702       * This will release the lock while calling out.
4703       *
4704       * @return true when started, false otherwise
4705       */

4706      public boolean startResource()
4707              throws XAException JavaDoc
4708      {
4709         int flags = XAResource.TMJOIN;
4710
4711         if (resourceSameRM == null)
4712         {
4713            switch (resourceState)
4714            {
4715            case RS_NEW:
4716               flags = XAResource.TMNOFLAGS;
4717               break;
4718            case RS_SUSPENDED:
4719               flags = XAResource.TMRESUME;
4720               break;
4721
4722            default:
4723               if (trace)
4724                  log.trace("Unhandled resource state: " + resourceState +
4725                            " (not RS_NEW or RS_SUSPENDED, using TMJOIN flags)");
4726            }
4727         }
4728
4729         if (trace)
4730            log.trace("startResource(" +
4731                      xidFactory.toString(resourceXid) +
4732                      ") entered: " + xaResource.toString() +
4733                      " flags=" + flags);
4734
4735         unlock();
4736         // OSH FIXME: resourceState could be incorrect during this callout.
4737
try
4738         {
4739            try
4740            {
4741               xaResource.start(resourceXid, flags);
4742            }
4743            catch (XAException JavaDoc e)
4744            {
4745               throw e;
4746            }
4747            catch (Throwable JavaDoc t)
4748            {
4749               if (trace)
4750                  log.trace("unhandled throwable error in startResource",
4751                            t);
4752               status = Status.STATUS_MARKED_ROLLBACK;
4753               return false;
4754            }
4755
4756            // Now the XA resource is associated with a transaction.
4757
resourceState = RS_ENLISTED;
4758         }
4759         finally
4760         {
4761            lock();
4762            if (trace)
4763               log.trace("startResource(" +
4764                         xidFactory.toString(resourceXid) +
4765                         ") leaving: " + xaResource.toString() +
4766                         " flags=" + flags);
4767         }
4768         return true;
4769      }
4770
4771      /**
4772       * Delist the resource unless we already did it
4773       */

4774      public boolean delistResource(XAResource JavaDoc xaRes, int flag)
4775              throws XAException JavaDoc
4776      {
4777         if (isDelisted(xaRes))
4778         {
4779            // This RM always returns false on isSameRM. Further,
4780
// the last resource has already been delisted.
4781
log.warn("Resource already delisted. tx=" +
4782                     TransactionImpl.this.toString());
4783            return false;
4784         }
4785         endResource(flag);
4786         return true;
4787      }
4788
4789      /**
4790       * End the resource
4791       */

4792      public void endResource()
4793              throws XAException JavaDoc
4794      {
4795         if (resourceState == RS_ENLISTED || resourceState == RS_SUSPENDED)
4796         {
4797            if (trace)
4798               log.trace("endresources(" + xaResource + "): state=" +
4799                         resourceState);
4800            endResource(XAResource.TMSUCCESS);
4801         }
4802      }
4803
4804      /**
4805       * Call <code>end()</code> on the XAResource and update
4806       * internal state information.
4807       * This will release the lock while calling out.
4808       *
4809       * @param flag The flag argument for the end() call.
4810       */

4811      private void endResource(int flag)
4812              throws XAException JavaDoc
4813      {
4814         if (trace)
4815            log.trace("endResource(" +
4816                      xidFactory.toString(resourceXid) +
4817                      ") entered: " + xaResource.toString() +
4818                      " flag=" + flag);
4819
4820         unlock();
4821         // OSH FIXME: resourceState could be incorrect during this callout.
4822
try
4823         {
4824            try
4825            {
4826               xaResource.end(resourceXid, flag);
4827            }
4828            catch (XAException JavaDoc e)
4829            {
4830               throw e;
4831            }
4832            catch (Throwable JavaDoc t)
4833            {
4834               if (trace)
4835                  log.trace("unhandled throwable error in endResource", t);
4836               status = Status.STATUS_MARKED_ROLLBACK;
4837               // Resource may or may not be ended after illegal exception.
4838
// We just assume it ended.
4839
resourceState = RS_ENDED;
4840               return;
4841            }
4842
4843            // Update our internal state information
4844
if (flag == XAResource.TMSUSPEND)
4845               resourceState = RS_SUSPENDED;
4846            else
4847            {
4848               if (flag == XAResource.TMFAIL)
4849                  status = Status.STATUS_MARKED_ROLLBACK;
4850               resourceState = RS_ENDED;
4851            }
4852         }
4853         finally
4854         {
4855            lock();
4856            if (trace)
4857               log.trace("endResource(" +
4858                         xidFactory.toString(resourceXid) +
4859                         ") leaving: " + xaResource.toString() +
4860                         " flag=" + flag);
4861         }
4862      }
4863
4864      /**
4865       * Remember that this resource has a heuristic outcome.
4866       */

4867      public void remember(int heuristicCode)
4868      {
4869         heurCode = heuristicCode;
4870         if (xaResourcesWithHeuristicDecisions == null)
4871            xaResourcesWithHeuristicDecisions = new ArrayList JavaDoc();
4872         xaResourcesWithHeuristicDecisions.add(this);
4873      }
4874      
4875      /**
4876       * Get the heuristic status of this resource.
4877       */

4878      public int getHeuristicCode()
4879      {
4880         return heurCode;
4881      }
4882      
4883      /**
4884       * Forget the resource
4885       */

4886      public void forget()
4887      {
4888         unlock();
4889         if (trace)
4890            log.trace("Forget: " + xaResource +
4891                      " xid=" + xidFactory.toString(resourceXid));
4892         try
4893         {
4894            xaResource.forget(resourceXid);
4895            resourceState = RS_FORGOT;
4896         }
4897         catch (XAException JavaDoc xae)
4898         {
4899            logXAException(xae);
4900            cause = xae;
4901            if (xae.errorCode == XAException.XAER_NOTA)
4902            {
4903               if (trace)
4904                  log.trace("XAER_NOTA in forget: " + xaResource +
4905                            " xid=" + xidFactory.toString(resourceXid) +
4906                            "\nAssuming that the xid has been previously " +
4907                            "forgotten.", xae);
4908               resourceState = RS_FORGOT;
4909            }
4910         }
4911         finally
4912         {
4913            lock();
4914         }
4915      }
4916
4917      /**
4918       * Prepare the resource
4919       */

4920      public int prepare()
4921              throws XAException JavaDoc
4922      {
4923         int vote;
4924         unlock();
4925         if (trace)
4926            log.trace("Prepare: " + xaResource +
4927                      " xid=" + xidFactory.toString(resourceXid));
4928         try
4929         {
4930            vote = xaResource.prepare(resourceXid);
4931            
4932            if (vote == XAResource.XA_OK)
4933               resourceState = RS_VOTE_OK;
4934            else if (vote == XAResource.XA_RDONLY)
4935               resourceState = RS_VOTE_READONLY;
4936         }
4937         catch (XAException JavaDoc e)
4938         {
4939            if (e.errorCode >= XAException.XA_RBBASE
4940                  && e.errorCode <= XAException.XA_RBEND)
4941            {
4942               if (trace)
4943                  log.trace("Got rollback vote from XAResource " + xaResource +
4944                            " xid=" + xidFactory.toString(resourceXid) +
4945                            ", tx=" + TransactionImpl.this.toString() +
4946                            ", errorCode=" +
4947                            TxUtils.getXAErrorCodeAsString(e.errorCode), e);
4948
4949               resourceState = RS_ROLLEDBACK;
4950            }
4951            else
4952               throw e;
4953         }
4954         finally
4955         {
4956            lock();
4957         }
4958         return resourceState;
4959      }
4960
4961      /**
4962       * Prepare the last resource
4963       */

4964      public void prepareLastResource()
4965              throws XAException JavaDoc
4966      {
4967         resourceState = RS_VOTE_OK;
4968      }
4969
4970      /**
4971       * Commit the resource
4972       */

4973      public void commit(boolean onePhase)
4974              throws XAException JavaDoc
4975      {
4976         if (!onePhase && resourceState != RS_VOTE_OK)
4977            return; // Voted read-only at prepare phase.
4978

4979         if (resourceSameRM != null)
4980            return; // This RM already committed.
4981

4982         unlock();
4983         if (trace)
4984            log.trace("Commit: " + xaResource +
4985                      " xid=" + xidFactory.toString(resourceXid) +
4986                      " onePhase=" + onePhase);
4987         try
4988         {
4989            xaResource.commit(resourceXid, onePhase);
4990            committedResources++;
4991            resourceState = RS_COMMITTED;
4992         }
4993         catch (XAException JavaDoc e)
4994         {
4995            switch (e.errorCode)
4996            {
4997            case XAException.XA_HEURCOM:
4998               // Ignore this exception, as the heuristic outcome
4999
// is the one we wanted anyway.
5000
logXAException(e);
5001               if (trace)
5002                  log.trace("Ignoring XAException.XA_HEURCOM" +
5003                            " in XAResource.commit: " + xaResource +
5004                            " xid=" + xidFactory.toString(resourceXid) +
5005                            " onePhase=" + onePhase);
5006               forget();
5007               committedResources++;
5008               resourceState = RS_COMMITTED;
5009               break;
5010            case XAException.XAER_RMERR:
5011            case XAException.XA_HEURRB:
5012               rolledbackResources++;
5013               // fall through
5014
case XAException.XA_HEURMIX:
5015            case XAException.XA_HEURHAZ:
5016               resourceState = RS_HEUR_OUTCOME;
5017               throw e;
5018            case XAException.XAER_RMFAIL:
5019            case XAException.XA_RETRY:
5020               // stay in RS_VOTE_OK state - the commit will be retried
5021
throw e;
5022            case XAException.XAER_NOTA:
5023            case XAException.XAER_INVAL:
5024            case XAException.XAER_PROTO:
5025            default:
5026               // no point retrying
5027
resourceState = RS_ERROR;
5028               throw e;
5029            }
5030         }
5031         finally
5032         {
5033            if (xaResourceAccess != null && resourceState != RS_VOTE_OK)
5034               xaResourceAccess.release();
5035            lock();
5036         }
5037      }
5038
5039      /**
5040       * Rollback the resource
5041       */

5042      public void rollback()
5043              throws XAException JavaDoc
5044      {
5045         if (resourceState == RS_VOTE_READONLY ||
5046               resourceState == RS_ROLLEDBACK || resourceState == RS_FORGOT)
5047            return;
5048
5049         if (resourceSameRM != null)
5050            return; // This RM already rolled back.
5051

5052         unlock();
5053         if (trace)
5054            log.trace("Rollback: " + xaResource +
5055                      " xid=" + xidFactory.toString(resourceXid));
5056         try
5057         {
5058            xaResource.rollback(resourceXid);
5059            rolledbackResources++;
5060            resourceState = RS_ROLLEDBACK;
5061         }
5062         catch (XAException JavaDoc e)
5063         {
5064            switch (e.errorCode)
5065            {
5066            case XAException.XAER_RMERR:
5067               // Ignore this exception, as the transaction has been
5068
// rolled back, which is what we wanted anyway.
5069
if (trace)
5070                  log.trace("Ignoring XAException.XAER_RMERR" +
5071                            " in XAResource.rollback: " + xaResource +
5072                            " xid=" + xidFactory.toString(resourceXid));
5073               rolledbackResources++;
5074               resourceState = RS_ROLLEDBACK;
5075               break;
5076            case XAException.XA_HEURRB:
5077               // Ignore this exception, as the heuristic outcome
5078
// is the one we wanted anyway.
5079
if (trace)
5080                  log.trace("Ignoring XAException.XA_HEURRB" +
5081                            " in XAResource.rollback: " + xaResource +
5082                            " xid=" + xidFactory.toString(resourceXid));
5083               forget();
5084               rolledbackResources++;
5085               resourceState = RS_ROLLEDBACK;
5086               break;
5087            case XAException.XA_HEURCOM:
5088               committedResources++;
5089            case XAException.XA_HEURMIX:
5090            case XAException.XA_HEURHAZ:
5091               resourceState = RS_HEUR_OUTCOME;
5092               throw e;
5093            case XAException.XAER_RMFAIL:
5094            case XAException.XA_RETRY:
5095               // stay in RS_VOTE_OK state - the rollback will be retried
5096
throw e;
5097            case XAException.XAER_NOTA:
5098            case XAException.XAER_INVAL:
5099            case XAException.XAER_PROTO:
5100            default:
5101               // no point retrying
5102
resourceState = RS_ERROR;
5103               throw e;
5104            }
5105         }
5106         finally
5107         {
5108            if (xaResourceAccess != null && resourceState != RS_VOTE_OK)
5109               xaResourceAccess.release();
5110            lock();
5111         }
5112      }
5113   }
5114
5115   /**
5116    * Represents a remote resource enlisted in the transaction.
5117    */

5118   private class EnlistedRemoteResource
5119           implements EnlistedResource
5120   {
5121      /**
5122       * The remote resource.
5123       */

5124      private Resource JavaDoc remoteResource;
5125
5126      /**
5127       * The state of the resource.
5128       */

5129      private int resourceState;
5130      
5131      /**
5132       * The heuristic status of this resource
5133       */

5134      private int heurCode;
5135
5136      /**
5137       * Creates a new <code>EnlistedRemoteResource</code>.
5138       */

5139      public EnlistedRemoteResource(Resource JavaDoc remoteResource)
5140      {
5141         this.remoteResource = remoteResource;
5142         resourceState = RS_NEW;
5143         heurCode = HEUR_NONE;
5144      }
5145
5146      /**
5147       * Creates a new <code>EnlistedRemoteResource</code>.
5148       * This constructor is intended to be used during recovery only.
5149       */

5150      public EnlistedRemoteResource(Resource JavaDoc remoteResource, boolean prepared)
5151      {
5152         this.remoteResource = remoteResource;
5153         if (prepared)
5154            resourceState = RS_VOTE_OK;
5155         else
5156            resourceState = RS_NEW;
5157         heurCode = HEUR_NONE;
5158      }
5159
5160      /**
5161       * Creates a resource in a heuristically completed state.
5162       * This constructor is intended to be used during recovery only.
5163       */

5164      public EnlistedRemoteResource(Resource JavaDoc remoteResource,
5165                                    boolean committed,
5166                                    int heurCode)
5167      {
5168         this.remoteResource = remoteResource;
5169         resourceState = (committed) ? RS_COMMITTED : RS_ROLLEDBACK;
5170         this.heurCode = heurCode;
5171      }
5172      
5173      /**
5174       * Gets the remote resource represented by this
5175       * <code>EnlistedRemoteResource</code> instance.
5176       */

5177      public Resource JavaDoc getRemoteResource()
5178      {
5179         return remoteResource;
5180      }
5181
5182      /**
5183       * Gets the state of the remote resource represented by this
5184       * <code>EnlistedRemoteResource</code> instance.
5185       */

5186      public int getState()
5187      {
5188         return resourceState;
5189      }
5190
5191      /**
5192       * Prepare the resource
5193       */

5194      public int prepare()
5195              throws RemoteException JavaDoc,
5196                     TransactionAlreadyPreparedException,
5197                     HeuristicMixedException JavaDoc,
5198                     HeuristicHazardException
5199      {
5200         Vote vote = null;
5201         unlock();
5202         try
5203         {
5204            vote = remoteResource.prepare();
5205         }
5206         finally
5207         {
5208            lock();
5209         }
5210
5211         if (vote == Vote.COMMIT)
5212            resourceState = RS_VOTE_OK;
5213         else if (vote == Vote.READONLY)
5214            resourceState = RS_VOTE_READONLY;
5215         else /* (vote == Vote.ROLLBACK) */
5216            resourceState = RS_ROLLEDBACK;
5217
5218         return resourceState;
5219      }
5220
5221      /**
5222       * Commit the resource
5223       *
5224       * @throws RemoteException
5225       * @throws HeuristicHazardException
5226       * @throws HeuristicMixedException
5227       * @throws HeuristicRollbackException
5228       * @throws TransactionNotPreparedException
5229       *
5230       */

5231      public void commit(boolean onePhase)
5232              throws RemoteException JavaDoc,
5233                     TransactionNotPreparedException,
5234                     HeuristicRollbackException JavaDoc,
5235                     HeuristicMixedException JavaDoc,
5236                     HeuristicHazardException
5237      {
5238         if (trace)
5239            log.trace("Committing resource " + remoteResource +
5240                      " state=" + resourceState);
5241
5242         if (!onePhase && resourceState != RS_VOTE_OK)
5243            return; // Voted read-only at prepare phase.
5244

5245         unlock();
5246         try
5247         {
5248            if (onePhase)
5249               remoteResource.commitOnePhase();
5250            else
5251               remoteResource.commit();
5252
5253            committedResources++;
5254            resourceState = RS_COMMITTED;
5255         }
5256         catch (TransactionRolledbackException JavaDoc e)
5257         {
5258            rolledbackResources++;
5259            resourceState = RS_ROLLEDBACK;
5260            throw e;
5261         }
5262         catch (RemoteException JavaDoc e)
5263         {
5264            // stay in RS_VOTE_OK state - the commit will be retried
5265
throw e;
5266         }
5267         catch (TransactionNotPreparedException e)
5268         {
5269            resourceState = RS_ERROR;
5270            throw e;
5271         }
5272         catch (HeuristicRollbackException JavaDoc e)
5273         {
5274            rolledbackResources++;
5275            resourceState = RS_HEUR_OUTCOME;
5276            throw e;
5277         }
5278         catch (HeuristicMixedException JavaDoc e)
5279         {
5280            resourceState = RS_HEUR_OUTCOME;
5281            throw e;
5282         }
5283         catch (HeuristicHazardException e)
5284         {
5285            resourceState = RS_HEUR_OUTCOME;
5286            throw e;
5287         }
5288         finally
5289         {
5290            lock();
5291         }
5292      }
5293
5294      /**
5295       * Rollback the resource
5296       */

5297      public void rollback()
5298              throws RemoteException JavaDoc,
5299                     HeuristicCommitException JavaDoc,
5300                     HeuristicMixedException JavaDoc,
5301                     HeuristicHazardException
5302      {
5303         if (resourceState == RS_VOTE_READONLY ||
5304               resourceState == RS_ROLLEDBACK || resourceState == RS_FORGOT)
5305            return;
5306
5307         unlock();
5308         try
5309         {
5310            remoteResource.rollback();
5311            rolledbackResources++;
5312            resourceState = RS_ROLLEDBACK;
5313         }
5314         catch (TransactionRolledbackException JavaDoc e)
5315         {
5316            rolledbackResources++;
5317            resourceState = RS_ROLLEDBACK;
5318            throw e;
5319         }
5320         catch (RemoteException JavaDoc e)
5321         {
5322            // stay in RS_VOTE_OK state - the rollback will be retried
5323
throw e;
5324         }
5325         catch (HeuristicCommitException JavaDoc e)
5326         {
5327            committedResources++;
5328            resourceState = RS_HEUR_OUTCOME;
5329            throw e;
5330         }
5331         catch (HeuristicMixedException JavaDoc e)
5332         {
5333            resourceState = RS_HEUR_OUTCOME;
5334            throw e;
5335         }
5336         catch (HeuristicHazardException e)
5337         {
5338            resourceState = RS_HEUR_OUTCOME;
5339            throw e;
5340         }
5341         finally
5342         {
5343            lock();
5344         }
5345      }
5346
5347      /**
5348       * Remember that this resource has a heuristic outcome.
5349       */

5350      public void remember(int heuristicCode)
5351      {
5352         heurCode = heuristicCode;
5353         if (remoteResourcesWithHeuristicDecisions == null)
5354            remoteResourcesWithHeuristicDecisions = new ArrayList JavaDoc();
5355         remoteResourcesWithHeuristicDecisions.add(this);
5356      }
5357      
5358      /**
5359       * Get the heuristic status of this resource.
5360       */

5361      public int getHeuristicCode()
5362      {
5363         return heurCode;
5364      }
5365      
5366      /**
5367       * Forget the resource
5368       */

5369      public void forget()
5370      {
5371         unlock();
5372         if (trace)
5373            log.trace("Forget: " + remoteResource);
5374         try
5375         {
5376            remoteResource.forget();
5377            resourceState = RS_FORGOT;
5378         }
5379         catch (NoSuchObjectException JavaDoc e)
5380         {
5381            if (trace)
5382               log.trace("NoSuchObjectException in forget: remoteResource=" +
5383                         remoteResource + "\nAssuming that the resource has " +
5384                         "been previously forgotten.", e);
5385         }
5386         catch (RemoteException JavaDoc e)
5387         {
5388            if (trace)
5389               log.trace("RemoteException in forget: remoteResource=" +
5390                         remoteResource + "\nThe resource still needs to be " +
5391                         "forgotten.", e);
5392         }
5393         finally
5394         {
5395            lock();
5396         }
5397         resourceState = RS_FORGOT;
5398      }
5399
5400   }
5401
5402}
5403
Popular Tags