001    /*
002     * @(#) SubCoordinator.java 
003     *
004     * JOTM: Java Open Transaction Manager 
005     *
006     *
007     * This module was originally developed by 
008     *
009     *  - Bull S.A. as part of the JOnAS application server code released in 
010     *    July 1999 (www.bull.com)
011     * 
012     * --------------------------------------------------------------------------
013     *  The original code and portions created by Bull SA are 
014     *  Copyright (c) 1999 BULL SA  
015     *  All rights reserved.
016     *  
017     * Redistribution and use in source and binary forms, with or without 
018     * modification, are permitted provided that the following conditions are met:
019     *
020     * -Redistributions of source code must retain the above copyright notice, this
021     * list of conditions and the following disclaimer. 
022     *
023     * -Redistributions in binary form must reproduce the above copyright notice, 
024     * this list of conditions and the following disclaimer in the documentation 
025     * and/or other materials provided with the distribution. 
026     *
027     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
028     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
029     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
030     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
031     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
032     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
033     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
034     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
035     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
036     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
037     * POSSIBILITY OF SUCH DAMAGE.
038     * 
039     * --------------------------------------------------------------------------
040     * Contributor(s):
041     * 01/11/06 Christophe Ney cney@batisseurs.com 
042     *          Added ResourceManagerListener mechanism to remove ThreadData
043     *          dependency.
044     *
045     * 02/06/18 Marek Prochazka
046     *          In addSynchronization() synchronizations are processed the
047     *          same way as resources. We add synchro although transaction is
048     *          marked rollback-only to be able to send beforeCompletion
049     *
050     * --------------------------------------------------------------------------
051     * $Id: SubCoordinator.java,v 1.43 2005-11-02 16:06:54 tonyortiz Exp $
052     * --------------------------------------------------------------------------
053     */
054    
055    package org.objectweb.jotm;
056    
057    import java.nio.ByteBuffer;
058    import java.rmi.RemoteException;
059    import java.util.List;
060    import java.util.Vector;
061    
062    import javax.rmi.PortableRemoteObject;
063    import javax.transaction.InvalidTransactionException;
064    import javax.transaction.RollbackException;
065    import javax.transaction.Status;
066    import javax.transaction.Synchronization;
067    import javax.transaction.SystemException;
068    import javax.transaction.Transaction;
069    import javax.transaction.TransactionManager;
070    import javax.transaction.TransactionRolledbackException;
071    import javax.transaction.xa.XAException;
072    import javax.transaction.xa.XAResource;
073    
074    import org.objectweb.howl.log.xa.XACommittingTx;
075    
076    /**
077     * Log associated to this transaction coordinator
078     */
079    class SLog {
080    
081            private Vector loggedResources = new Vector();
082            private Vector loggedByteResources = new Vector();
083    
084            private Vector loggedXids = new Vector();
085    
086            private Vector loggedJavaxXids = new Vector();
087    
088            int decision;
089            static final int DECISION_TO_COMMIT = 1;
090            static final int DECISION_TO_ROLLBACK = 2;
091    
092            public void addResource(XAResource res, Xid xid, javax.transaction.xa.Xid javaxxid) {
093                    if (TraceTm.jta.isDebugEnabled()) {
094                            TraceTm.jta.debug("res= " + res);
095                            TraceTm.jta.debug("xid= " + xid);
096                            TraceTm.jta.debug("javaxxid= " + javaxxid);
097                    }
098                    loggedResources.addElement(res);
099                    loggedByteResources.addElement(res.toString().getBytes());
100                    loggedXids.addElement(xid);
101                    loggedJavaxXids.addElement(javaxxid);
102            }
103    
104            /**
105             * @return a List of logged Resources
106             */
107            public List getLoggedResources() {
108                    if (TraceTm.jta.isDebugEnabled()) {
109                            TraceTm.jta.debug("logged resources=" + loggedResources);
110                    }
111                    return loggedResources;
112            }
113    
114            public List getByteLoggedResources() {
115                    if (TraceTm.jta.isDebugEnabled()) {
116                            TraceTm.jta.debug("logged resources=" + loggedResources);
117                    }
118                    return loggedByteResources;
119            }
120    
121            public List getLoggedXids() {
122                    return loggedXids;
123            }
124    
125            public List getLoggedJavaxXids() {
126                    return loggedJavaxXids;
127            }
128    
129            public void flushLog(int decide) {
130                    if (TraceTm.jta.isDebugEnabled()) {
131                            TraceTm.jta.debug("decide=" + decide);
132                    }
133                    decision = decide;
134    
135                    // XXX serialize log on disk
136            }
137    
138            public void forgetLog() {
139                    if (TraceTm.jta.isDebugEnabled()) {
140                            TraceTm.jta.debug("forget log");
141                    }
142                    // XXX remove file on disk
143            }
144    }
145    
146    class JotmTransactionRolledbackException extends TransactionRolledbackException {
147            private static final long serialVersionUID = 1L;
148    
149            public JotmTransactionRolledbackException() {
150                    super();
151            }
152    
153            public JotmTransactionRolledbackException(String message) {
154                    super(message);
155            }
156    
157            public JotmTransactionRolledbackException(String message, Throwable cause) {
158                    super(message);
159                    detail = cause;
160            }
161    
162            public JotmTransactionRolledbackException(Throwable cause) {
163                    super();
164                    detail = cause;
165            }
166    }
167    
168    /**
169     * This object is the local coordinator. It may be registered as sub-coordinator in case of distributed transaction, so
170     * it must be callable remotely and implement Resource
171     */
172    public class SubCoordinator extends PortableRemoteObject implements Resource {
173    
174            // ------------------------------------------------------------------
175            // Object state
176            // ------------------------------------------------------------------
177    
178            /**
179             * @serial
180             */
181            private TransactionImpl tx = null;
182    
183            /**
184             * List of Synchronization objects TODO should synchronize this list.
185             * 
186             * @serial
187             */
188            private Vector synchroList = new Vector();
189    
190            /**
191             * List of XAResource objects
192             * 
193             * @serial
194             */
195            private Vector resourceList = new Vector();
196            private Vector javaxxidList = new Vector();
197    
198            /**
199             * Keep a reference on TransactionManager
200             * 
201             * @serial
202             */
203            private TransactionManager tm;
204    
205            /**
206             * @serial
207             */
208            private Xid xid = null;
209    
210            /**
211             * @serial
212             */
213            private SLog log = null;
214    
215            /**
216             * javax.transaction.Status
217             * <dl>
218             * <dt>ACTIVE</dt>
219             * <dd>transaction started, commit phase not started</dd>
220             * <dt>PREPARING</dt>
221             * <dd>prepare is being sent to resources</dd>
222             * <dt>PREPARED</dt>
223             * <dd>prepare is done. Must commit now</dd>
224             * <dt>COMMITTING</dt>
225             * <dd>commit is being sent to resources</dd>
226             * <dt>COMMITTED</dt>
227             * <dd>commit was successful</dd>
228             * <dt>ROLLING_BACK</dt>
229             * <dd>not used</dd>
230             * <dt>MARKED_ROLLBACK</dt>
231             * <dd>setRollbackOnly has been called</dd>
232             * <dt>ROLLEDBACK</dt>
233             * <dd>transaction has been rolled back (or prepare returned "vote_rollback")</dd>
234             * <dt>UNKNOWN</dt>
235             * <dd>commit raised heuristics</dd>
236             * <dt>NO_TRANSACTION</dt>
237             * <dd>cannot get the status value from JTM</dd>
238             * </dl>
239             * 
240             * @serial
241             */
242            private int status = Status.STATUS_ACTIVE;
243    
244            private Exception rollbackCause;
245            /**
246             * @serial
247             */
248            private boolean beforeCompletionDone = false;
249    
250            static final String JOTM_RD_ONLY = "JOTM_RD_ONLY";
251    
252            static final byte[] RT1 = "RR1".getBytes();
253            static final byte[] RT2 = "RR2".getBytes();
254            static final byte[] JOTMDONE = "RR3JOTMDONE".getBytes();
255    
256            // ------------------------------------------------------------------
257            // Constructors
258            // ------------------------------------------------------------------
259    
260            /**
261             * constructor used by TransactionImpl
262             */
263            SubCoordinator(TransactionImpl tx, Xid xid) throws RemoteException {
264    
265                    if (TraceTm.jta.isDebugEnabled()) {
266                            TraceTm.jta.debug("tx=" + tx + ",  xid=" + xid);
267                    }
268    
269                    this.tx = tx;
270                    this.xid = xid;
271                    this.tm = Current.getTransactionManager();
272    
273                    beforeCompletionDone = false;
274            }
275    
276            // ------------------------------------------------------------------
277            // Resource interface
278            // ------------------------------------------------------------------
279    
280            /**
281             * phase 1 of the 2PC.
282             * 
283             * @return int vote commit, rollback, or readonly.
284             */
285            @Override
286            public int prepare() throws RemoteException {
287    
288                    if (TraceTm.jta.isDebugEnabled()) {
289                            TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status));
290                    }
291    
292                    try {
293                            tx.doDetach(XAResource.TMSUCCESS);
294                    } catch (SystemException e) {
295    
296                            if (TraceTm.jta.isDebugEnabled()) {
297                                    String error = "Error when detaching XAResource:" + e + "--" + e.getMessage();
298                                    TraceTm.jta.debug(error);
299                            }
300                    }
301    
302                    switch (status) {
303                    case Status.STATUS_MARKED_ROLLBACK:
304                            // Doing rollback now may be an issue, because the following rollback
305                            // will not work (this object do not exist any longer)
306                            // doBeforeCompletion(false);
307                            // doRollback();
308                            status = Status.STATUS_ROLLING_BACK;
309                            return Resource.VOTE_ROLLBACK;
310                    case Status.STATUS_COMMITTED:
311                            return Resource.VOTE_COMMIT;
312                    default:
313                            doBeforeCompletion(true);
314                            break;
315                    }
316    
317                    // Recheck Status after doBeforeCompletion
318    
319                    if (status == Status.STATUS_MARKED_ROLLBACK) {
320                            TraceTm.jotm.info("Rollback during beforeCompletion in SubCoordinator.prepare");
321                            // doRollback();
322                            status = Status.STATUS_ROLLING_BACK;
323                            return Resource.VOTE_ROLLBACK;
324                    }
325    
326                    int ret = doPrepare();
327    
328                    if (ret == Resource.VOTE_READONLY) {
329                            // Transaction completed for this Resource
330                            doAfterCompletion();
331                    }
332    
333                    if (TraceTm.jta.isDebugEnabled()) {
334                            TraceTm.jta.debug("vote = " + ret);
335                    }
336    
337                    return ret;
338            }
339    
340            /**
341             * rollback transaction
342             */
343    
344            @Override
345            public void rollback() throws RemoteException {
346    
347                    if (TraceTm.jta.isDebugEnabled()) {
348                            TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status));
349                    }
350    
351                    try {
352                            tx.doDetach(XAResource.TMSUCCESS);
353                    } catch (SystemException e) {
354                            if (TraceTm.jta.isDebugEnabled()) {
355                                    String error = "Error when detaching XAResource:" + e + "--" + e.getMessage();
356                                    TraceTm.jta.debug(error);
357                            }
358                    }
359    
360                    switch (status) {
361                    case Status.STATUS_ACTIVE:
362                    case Status.STATUS_MARKED_ROLLBACK:
363                    case Status.STATUS_ROLLING_BACK:
364                            if (TraceTm.jotm.isDebugEnabled()) {
365                                    TraceTm.jotm.debug("transaction rolling back");
366                            }
367                            break;
368                    case Status.STATUS_PREPARED:
369                            if (TraceTm.jotm.isDebugEnabled()) {
370                                    TraceTm.jotm.debug("should not rollback a prepared transaction");
371                            }
372                            break;
373                    case Status.STATUS_ROLLEDBACK:
374                            if (TraceTm.jotm.isDebugEnabled()) {
375                                    TraceTm.jotm.debug("already rolledback");
376                            }
377                            return;
378                    default:
379                            TraceTm.jotm.error("rollback: bad status: " + StatusHelper.getStatusName(status));
380                            return;
381                    }
382                    doBeforeCompletion(false);
383                    doRollback();
384            }
385    
386            /**
387             * phase 2 of the 2PC.
388             */
389            @Override
390            public void commit() throws RemoteException {
391    
392                    if (TraceTm.jta.isDebugEnabled()) {
393                            TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status));
394                    }
395    
396                    switch (status) {
397                    case Status.STATUS_PREPARED:
398                            break;
399                    default:
400                            TraceTm.jotm.error("commit: bad status: " + StatusHelper.getStatusName(status));
401                            return;
402                    }
403                    doCommit();
404            }
405    
406            /**
407             * commit 1 phase. Called either from JTM (distributed transaction) or from Transaction.commit (local transaction).
408             */
409    
410            @Override
411            public void commit_one_phase() throws RemoteException {
412    
413                    if (TraceTm.jta.isDebugEnabled()) {
414                            TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status));
415                    }
416    
417                    switch (status) {
418                    case Status.STATUS_ROLLEDBACK:
419                            try {
420                                    tx.doDetach(XAResource.TMSUCCESS);
421                            } catch (SystemException e) {
422    
423                                    if (TraceTm.jta.isDebugEnabled()) {
424                                            String error = "Error when detaching XAResource:" + e + "--" + e.getMessage();
425                                            TraceTm.jta.debug(error);
426                                    }
427                            }
428                            throw new JotmTransactionRolledbackException(rollbackCause);
429                    case Status.STATUS_MARKED_ROLLBACK:
430                            doBeforeCompletion(false);
431                            try {
432                                    tx.doDetach(XAResource.TMSUCCESS);
433                            } catch (SystemException e) {
434    
435                                    if (TraceTm.jta.isDebugEnabled()) {
436                                            String error = "Error when detaching XAResource:" + e + "--" + e.getMessage();
437                                            TraceTm.jta.debug(error);
438                                    }
439                            }
440                            doRollback();
441                            throw new JotmTransactionRolledbackException(rollbackCause);
442                    case Status.STATUS_COMMITTED:
443                            try {
444                                    tx.doDetach(XAResource.TMSUCCESS);
445                            } catch (SystemException e) {
446    
447                                    if (TraceTm.jta.isDebugEnabled()) {
448                                            String error = "Error when detaching XAResource:" + e + "--" + e.getMessage();
449                                            TraceTm.jta.debug(error);
450                                    }
451                            }
452                            return;
453                    default:
454                            doBeforeCompletion(true);
455                            try {
456                                    tx.doDetach(XAResource.TMSUCCESS);
457                            } catch (SystemException e) {
458    
459                                    if (TraceTm.jta.isDebugEnabled()) {
460                                            String error = "Error when detaching XAResource:" + e + "--" + e.getMessage();
461                                            TraceTm.jta.debug(error);
462                                    }
463                            }
464                            break;
465                    }
466    
467                    // status many have changed in doBeforeCompletion
468    
469                    if (TraceTm.jta.isDebugEnabled()) {
470                            TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status));
471                    }
472    
473                    // Recheck Status after doBeforeCompletion
474    
475                    if (status == Status.STATUS_MARKED_ROLLBACK) {
476                            TraceTm.jotm.info("Rollback during beforeCompletion in SubCoordinator.commit_one_phase");
477                            doRollback();
478                            throw new JotmTransactionRolledbackException(rollbackCause);
479                    }
480    
481                    // only 1 Resource => 1 phase commit
482    
483                    if (resourceList.size() == 1) {
484                            doOnePhaseCommit();
485                            return;
486                    }
487    
488                    // 2 phase commit
489    
490                    int vote = doPrepare();
491    
492                    switch (vote) {
493                    case Resource.VOTE_COMMIT:
494                            doCommit();
495                            break;
496                    case Resource.VOTE_READONLY:
497                            doAfterCompletion();
498                            break;
499                    case Resource.VOTE_ROLLBACK:
500                            doRollback();
501                            throw new JotmTransactionRolledbackException(rollbackCause);
502                    }
503            }
504    
505            /**
506             * forget heuristics about this transaction.
507             */
508            @Override
509            public void forget() throws RemoteException {
510                    if (TraceTm.jta.isDebugEnabled()) {
511                            TraceTm.jta.debug("SubCoordinator.forget()");
512                    }
513                    doForget();
514            }
515    
516            // ------------------------------------------------------------------
517            // Other public methods (interface exposed to Transaction object)
518            // ------------------------------------------------------------------
519    
520            /**
521             * add a Synchronization to the list
522             * 
523             * @param synchro
524             *            The javax.transaction.Synchronization object for the transaction associated with the target object
525             * 
526             * @exception RollbackException
527             *                Thrown to indicate that the transaction has been marked for rollback only.
528             * 
529             * @exception IllegalStateException
530             *                Thrown if the transaction in the target object is in prepared state or the transaction is
531             *                inactive.
532             * 
533             */
534            public void addSynchronization(Synchronization synchro) throws RollbackException, IllegalStateException {
535    
536                    if (TraceTm.jta.isDebugEnabled()) {
537                            TraceTm.jta.debug("synchro=" + synchro);
538                            TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status));
539                    }
540    
541                    // check status: should be ACTIVE.
542                    boolean markedRollback = false;
543    
544                    switch (status) {
545                    case Status.STATUS_MARKED_ROLLBACK:
546                    case Status.STATUS_ROLLEDBACK:
547                            markedRollback = true;
548                            break;
549                    case Status.STATUS_ACTIVE:
550                            break;
551                    default:
552                            String errorMsg = "addSynchronization: bad status = " + StatusHelper.getStatusName(status);
553                            TraceTm.jotm.error(errorMsg);
554                            throw new IllegalStateException(errorMsg);
555                    }
556    
557                    // Add synchro to the list of local synchros
558                    synchroList.addElement(synchro);
559    
560                    // If transaction marked rollback only, we add synchro but we throw
561                    // the correct exception because nobody can presume what will be done in the
562                    // Synchronization object, so we must send it beforeCompletion and afterCompletion
563                    // even in case of rollback.
564    
565                    if (markedRollback) {
566                            if (TraceTm.jta.isDebugEnabled()) {
567                                    TraceTm.jta.debug("SubCoordinator.addSynchronization: transaction rollback only");
568                            }
569                            throw new RollbackException();
570                    }
571            }
572    
573            /**
574             * add a XAResource to the list
575             * 
576             * @param xares
577             *            XAResource to register
578             * 
579             * @return true if this datasource was already known
580             * 
581             * @exception IllegalStateException
582             *                Thrown if the transaction in the target object is in prepared state or the transaction is
583             *                inactive.
584             */
585            public synchronized boolean addResource(XAResource xares) throws IllegalStateException {
586    
587                    if (TraceTm.jta.isDebugEnabled()) {
588                            TraceTm.jta.debug("xares=" + xares);
589                            TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status));
590                    }
591    
592                    // check status: should be ACTIVE.
593                    boolean markedRollback = false;
594    
595                    switch (status) {
596                    case Status.STATUS_MARKED_ROLLBACK:
597                            markedRollback = true;
598                            break;
599                    case Status.STATUS_ACTIVE:
600                            break;
601                    default:
602                            String errorMsg = "SubCoordinator.addResource: bad status= " + StatusHelper.getStatusName(status);
603                            TraceTm.jotm.error(errorMsg);
604                            throw new IllegalStateException(errorMsg);
605                    }
606    
607                    // Check if this DataSource is already known
608                    // -> we must register only ONE resource per DataSource.
609    
610                    boolean found = false;
611    
612                    for (int i = 0; i < resourceList.size(); i++) {
613                            XAResource res = (XAResource) resourceList.elementAt(i);
614    
615                            try {
616                                    if (res.isSameRM(xares)) {
617                                            found = true;
618                                            break;
619                                    }
620                            } catch (XAException e) {
621                                    String error = "Cannot send res.isSameRM:" + e + " (error code = " + e.errorCode + ") --"
622                                                    + e.getMessage();
623                                    TraceTm.jotm.error("Exception on resource.isSameRM: " + error);
624                            }
625                    }
626    
627                    // add this XAResource to the list
628    
629                    if (found == false) {
630                            if (TraceTm.jta.isDebugEnabled()) {
631                                    TraceTm.jta.debug("new XAResource added to the list");
632                            }
633                            resourceList.addElement(xares);
634                    }
635    
636                    // If transaction marked rollback only, we enlist resource but we throw
637                    // the correct exception. It is important to enlist the Resource because
638                    // if we don't, the database would be updated although transaction has been
639                    // rolled back.
640    
641                    if (markedRollback) {
642                            TraceTm.jta.debug("SubCoordinator.addResource: transaction set rollback only");
643                    }
644    
645                    return found;
646            }
647    
648            /**
649             * add this javaxxid to the List
650             * 
651             * @param javaxxid
652             */
653            public synchronized void addJavaxXid(javax.transaction.xa.Xid javaxxid) {
654                    if (TraceTm.jta.isDebugEnabled()) {
655                            TraceTm.jta.debug("addJavaxXid javaxxid=" + javaxxid);
656                    }
657    
658                    // add this javaxxid to the List at the index location of XAResource
659                    javaxxidList.addElement(javaxxid);
660                    if (TraceTm.jta.isDebugEnabled()) {
661                            TraceTm.jta.debug("new JavaxXid added to the list");
662                    }
663            }
664    
665            /**
666             * Get the javaxxid at specified index in the list
667             * 
668             * @param xaresindex
669             * @return javaxxid
670             */
671            public javax.transaction.xa.Xid getJavaxXid(int xaresindex) {
672                    javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(xaresindex);
673                    return myjavaxxid;
674            }
675    
676            /**
677             * return the status of this transaction
678             */
679            public int getStatus() {
680                    if (TraceTm.jta.isDebugEnabled()) {
681                            TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status));
682                    }
683                    return status;
684            }
685    
686            /**
687             * set the transaction "rollback only"
688             */
689            public void setRollbackOnly() {
690    
691                    if (TraceTm.jta.isDebugEnabled()) {
692                            TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status));
693                    }
694    
695                    switch (status) {
696                    case Status.STATUS_ACTIVE:
697                    case Status.STATUS_UNKNOWN:
698                    case Status.STATUS_PREPARING:
699                            status = Status.STATUS_MARKED_ROLLBACK;
700                            break;
701                    case Status.STATUS_MARKED_ROLLBACK:
702                    case Status.STATUS_ROLLING_BACK:
703                            break;
704                    case Status.STATUS_PREPARED:
705                    case Status.STATUS_COMMITTED:
706                    case Status.STATUS_ROLLEDBACK:
707                    case Status.STATUS_NO_TRANSACTION:
708                    case Status.STATUS_COMMITTING:
709                            TraceTm.jotm.error("Cannot set transaction as rollback only");
710                            TraceTm.jotm.error("Bad status=" + StatusHelper.getStatusName(status));
711                            break;
712                    }
713            }
714    
715            // ------------------------------------------------------------------
716            // private methods
717            // ------------------------------------------------------------------
718    
719            /**
720             * forget Transaction
721             */
722            private void doForget() throws RemoteException {
723                    if (TraceTm.jta.isDebugEnabled()) {
724                            TraceTm.jta.debug("SubCoordinator.doForget()");
725                    }
726    
727                    boolean exception = false;
728    
729                    for (int i = 0; i < resourceList.size(); i++) {
730                            // Tell this Resource to forget the transaction
731                            // Remove from the list ?
732                            XAResource xar = (XAResource) resourceList.elementAt(i);
733    
734                            javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(i);
735    
736                            if (TraceTm.jta.isDebugEnabled()) {
737                                    TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
738                                    TraceTm.jta.debug("forgotten with resource= " + xar);
739                            }
740    
741                            try {
742                                    xar.forget(myjavaxxid);
743                            } catch (XAException e) {
744                                    String error = "Cannot send xar.forget:" + e + " (error code = " + e.errorCode + ") --"
745                                                    + e.getMessage();
746                                    TraceTm.jotm.error("Got XAException from xar.forget: " + error);
747                                    exception = true;
748                            }
749                    }
750    
751                    if (exception) {
752                            throw new RemoteException("XAException on forget");
753                    }
754    
755                    unexportObject(this);
756            }
757    
758            /**
759             * 2PC phase 1 (prepare) Basically, send prepare on each XAResource and collect the results.
760             */
761            private synchronized int doPrepare() throws RemoteException {
762                    if (TraceTm.jta.isDebugEnabled()) {
763                            TraceTm.jta.debug("SubCoordinator.doPrepare()");
764                    }
765    
766                    int ret = VOTE_READONLY;
767                    int errors = 0;
768    
769                    // No resource
770    
771                    if (resourceList.size() == 0) {
772                            // increment counter for management
773                            Current.getCurrent().incrementCommitCounter();
774                            status = Status.STATUS_COMMITTED;
775                            return ret;
776                    }
777    
778                    // Creates a log for that transaction, where we will add all the
779                    // resources that replied VOTE_COMMIT to prepare.
780                    // Do not flush the log on disk before decision to commit.
781    
782                    log = new SLog();
783    
784                    // Sends prepare to each resource while no error
785                    // In case of prepare on sub-coord. we may have only 1 resource.
786    
787                    status = Status.STATUS_PREPARING;
788    
789                    for (int i = 0; i < resourceList.size(); i++) {
790                            XAResource res = (XAResource) resourceList.elementAt(i);
791                            javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(i);
792    
793                            Xid xid = new XidImpl(this.xid, i);
794    
795                            if (errors > 0) {
796    
797                                    if (TraceTm.jta.isDebugEnabled()) {
798                                            TraceTm.jta.debug("xid=        " + xid);
799                                            TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
800                                            TraceTm.jta.debug("rolled back with resource= " + res);
801                                    }
802    
803                                    try {
804                                            res.rollback(myjavaxxid);
805                                            // res.rollback(xid);
806                                    } catch (XAException e) {
807                                            String error = "Cannot send res.rollback:" + e + " (error code = " + e.errorCode + ") --"
808                                                            + e.getMessage();
809                                            TraceTm.jotm.error("Got XAException from res.rollback: " + error);
810                                            // Nothing to do ?
811                                    }
812                            } else {
813                                    if (TraceTm.jta.isDebugEnabled()) {
814                                            TraceTm.jta.debug("xid=        " + xid);
815                                            TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
816                                            TraceTm.jta.debug("prepared with resource= " + res);
817                                    }
818    
819                                    try {
820                                            switch (res.prepare(myjavaxxid)) {
821                                            // switch (res.prepare(xid)) {
822                                            case XAResource.XA_OK:
823                                                    log.addResource(res, xid, myjavaxxid);
824                                                    ret = VOTE_COMMIT;
825                                                    if (TraceTm.jta.isDebugEnabled()) {
826                                                            TraceTm.jta.debug("Prepare= XA_OK");
827                                                    }
828                                                    break;
829                                            case XAResource.XA_RDONLY:
830                                                    if (TraceTm.jta.isDebugEnabled()) {
831                                                            TraceTm.jta.debug("Prepare= XA_RDONLY");
832                                                    }
833                                                    break;
834                                            }
835                                    } catch (XAException e) {
836                                            String error = "Cannot send res.prepare:" + e + " (error code = " + e.errorCode + ") --"
837                                                            + e.getMessage();
838                                            TraceTm.jotm.error("Got XAException from res.prepare: " + error);
839                                            ret = VOTE_ROLLBACK;
840                                            errors++;
841                                    }
842                            }
843                    }
844    
845                    // Update the status, depending on vote result
846                    // If all resources returned READ_ONLY, we can forget the transaction
847    
848                    switch (ret) {
849                    case VOTE_READONLY:
850                            // increment counter for management
851                            Current.getCurrent().incrementCommitCounter();
852                            status = Status.STATUS_COMMITTED;
853                            if (TraceTm.jta.isDebugEnabled()) {
854                                    TraceTm.jta.debug("VOTE_READONLY");
855                            }
856                            break;
857                    case VOTE_COMMIT:
858                            status = Status.STATUS_PREPARED;
859                            if (TraceTm.jta.isDebugEnabled()) {
860                                    TraceTm.jta.debug("VOTE_COMMIT");
861                            }
862                            break;
863                    case VOTE_ROLLBACK:
864                            status = Status.STATUS_ROLLING_BACK;
865                            if (TraceTm.jta.isDebugEnabled()) {
866                                    TraceTm.jta.debug("VOTE_ROLLBACK");
867                            }
868                            break;
869                    }
870    
871                    // return the global vote
872                    return ret;
873            }
874    
875            /**
876             * 2PC - phase 2 (commit) See JTM for heuristics management
877             */
878            private synchronized int doCommit() throws RemoteException {
879                    if (TraceTm.jta.isDebugEnabled()) {
880                            TraceTm.jta.debug("SubCoordinator.doCommit()");
881                    }
882    
883                    // We build the Recovery Record in doCommit just in case of a system crash
884                    // Store the Recovery Record using HOWL so it can manage for us.
885                    //
886                    // The Recovery Record consists of two record types:
887                    // 1. XA Transaction recovery record
888                    // 2. XA Resource recovery record
889                    //
890                    // The XA Transaction recovery record format:
891                    // recovery record type1 (byte[3]) - 'RR1'
892                    // recovery record stored date-time (long) - 8 bytes
893                    // format id (int) - 4 bytes
894                    // length of transaction's gtrid (int) - 4 bytes
895                    // global transaction id (byte []) - txxidgti.length bytes
896                    // length of transaction's bqual (int) - 4 bytes
897                    // bqual (byte []) - txxidbq.length bytes
898                    // length of transactions store date-time (int) - 4 bytes
899                    // transactions created date-time (byte []) - Date.length bytes
900                    // count of XA resources assigned to the transaction (int) - 4 bytes
901                    //
902                    // The XA Resource recovery record format:
903                    // recovery record type2 (byte[3]) = 'RR2'
904                    // resource index (int)
905                    // length of XA resource (int) - 4 bytes
906                    // XA resource (byte []) - xares.length bytes
907                    // length of XA resource class name (int) - 4 bytes
908                    // XA resource class name (byte []) - resclassname.length bytes
909                    // XID.formatid (int) - 4 bytes
910                    // length of XID.gtrid assigned to XA resource (int) - 4 bytes
911                    // XID.gtrid assigned to XA resource (byte []) - xidgti.length bytes
912                    // length of XID.bqual assigned to XA resource (int) - 4 bytes
913                    // XID.bqual assigned to XA resource (byte []) - xidbq.length bytes
914                    // XID status-state (int) = 4 bytes
915                    //
916                    // The JOTM Done recovery record format:
917                    // recovery record type3 (byte[3]) = 'RR3'
918                    // JOTM done value (byte[8]) = 'JOTMDONE'
919    
920                    // First check that a log is initialized
921    
922                    if (log == null) {
923                            TraceTm.jotm.error("doCommit: no log");
924                            return -1;
925                    }
926    
927                    boolean successfulcommit = true;
928                    int commitnb = 0;
929                    int heuristicnb = 0;
930    
931                    List loggedResources = log.getLoggedResources();
932                    List byteloggedResources = log.getByteLoggedResources();
933                    List loggedXids = log.getLoggedXids();
934                    List loggedJavaxXids = log.getLoggedJavaxXids();
935    
936                    XACommittingTx xaCommitTx = null;
937                    XACommittingTx xaCommitTxRewrite = null;
938    
939                    byte[][] recoveryBuffer = new byte[loggedResources.size() + 1][]; // loggedResources + 1 (recoveryRecord1)
940    
941                    byte[] recoveryRecord1 = null;
942                    byte[] recoveryRecord2 = null;
943                    ByteBuffer rr1 = null;
944                    ByteBuffer rr2 = null;
945    
946                    byte[][] jotmDoneRecord = new byte[1][11];
947    
948                    if (Current.getDefaultRecovery()) {
949                            Xid txxid = tx.getXid();
950    
951                            int txxidfi = txxid.getFormatId();
952                            byte[] txxidgti = txxid.getGlobalTransactionId();
953                            byte[] txxidbq = txxid.getBranchQualifier();
954                            int txxidlength = 4 + txxidgti.length + txxidbq.length;
955    
956                            // formatid(int) + gridlen(int) + grid + bqlen(int) + bqual
957                            int gridlen = txxidgti.length;
958                            int bqlen = txxidbq.length;
959    
960                            String txdate = tx.getTxDate().toString();
961                            int txdatelength = txdate.length();
962                            long rcdate = System.currentTimeMillis();
963    
964                            recoveryRecord1 = new byte[3 + 8 + 4 + 4 + gridlen + 4 + bqlen + 4 + txdatelength + 4];
965    
966                            rr1 = ByteBuffer.wrap(recoveryRecord1);
967                            rr1.put(RT1);
968                            rr1.putLong(rcdate);
969                            rr1.putInt(txxidfi);
970                            rr1.putInt(txxidgti.length);
971                            rr1.put(txxidgti);
972                            rr1.putInt(txxidbq.length);
973                            rr1.put(txxidbq);
974                            rr1.putInt(txdatelength);
975                            rr1.put(txdate.getBytes());
976                            rr1.putInt(loggedResources.size());
977    
978                            recoveryBuffer[0] = rr1.array();
979    
980                            for (int i = 0; i < loggedResources.size(); i++) {
981                                    XAResource res = (XAResource) loggedResources.get(i);
982                                    byte[] resname = (byte[]) byteloggedResources.get(i);
983    
984                                    Xid xid = (Xid) loggedXids.get(i);
985                                    int rmindex = 99; // Store 99, at recovery we will store the correct index
986    
987                                    if (TraceTm.recovery.isDebugEnabled()) {
988                                            TraceTm.recovery.debug("recovery xid= " + xid);
989                                            TraceTm.recovery.debug("recovery resource= " + res);
990                                    }
991    
992                                    int resnamelength = resname.length;
993                                    String resclassname = res.getClass().getName();
994                                    int resclassnamelength = resclassname.length();
995    
996                                    int xidfi = xid.getFormatId();
997                                    byte[] xidgti = xid.getGlobalTransactionId();
998                                    byte[] xidbq = xid.getBranchQualifier();
999                                    int rr2gtilen = xidgti.length;
1000                                    int rr2bqlen = xidbq.length;
1001    
1002                                    // formatid + : + gridlen + : + bqlen + : + xidgti + : + xidbq
1003    
1004                                    recoveryRecord2 = new byte[3 + 4 + 4 + resnamelength + 4 + resclassnamelength + 4 + 4 + rr2gtilen + 4
1005                                                    + rr2bqlen + 4];
1006    
1007                                    rr2 = ByteBuffer.wrap(recoveryRecord2);
1008                                    rr2.put(RT2);
1009                                    rr2.putInt(rmindex);
1010                                    rr2.putInt(resnamelength);
1011                                    rr2.put(resname);
1012                                    rr2.putInt(resclassnamelength);
1013                                    rr2.put(resclassname.getBytes());
1014                                    rr2.putInt(xidfi);
1015                                    rr2.putInt(xidgti.length);
1016                                    rr2.put(xidgti);
1017                                    rr2.putInt(xidbq.length);
1018                                    rr2.put(xidbq);
1019                                    rr2.putInt(status);
1020    
1021                                    if (TraceTm.recovery.isDebugEnabled()) {
1022                                            TraceTm.recovery.debug("Prepare Init RR2 to Recovery Buffer");
1023                                    }
1024                                    recoveryBuffer[i + 1] = rr2.array(); // First record (0) is always rr1
1025                            }
1026    
1027                            try {
1028                                    xaCommitTx = TransactionRecoveryImpl.getTransactionRecovery().howlCommitLog(recoveryBuffer);
1029                            } catch (Exception e) {
1030                                    // If we cannot write the Log, we cannot perform recovery, rollback transaction
1031                                    status = Status.STATUS_ROLLEDBACK;
1032    
1033                                    String howlerror = "Cannot howlCommitLog:" + e + " --" + e.getMessage();
1034                                    TraceTm.jotm.error("Got LogException from howlCommitLog: " + howlerror);
1035                                    xaCommitTx = null;
1036                                    doAfterCompletion();
1037                                    log.forgetLog();
1038    
1039                                    throw new JotmTransactionRolledbackException(e);
1040                            }
1041                    }
1042    
1043                    // Status Transaction = committing
1044    
1045                    status = Status.STATUS_COMMITTING;
1046                    if (TraceTm.recovery.isDebugEnabled()) {
1047                            TraceTm.recovery.debug("Status Committing");
1048                    }
1049    
1050                    // Send commit to each resource prepared
1051    
1052                    for (int i = 0; i < loggedResources.size(); i++) {
1053                            XAResource res = (XAResource) loggedResources.get(i);
1054    
1055                            // javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(i);
1056                            javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) loggedJavaxXids.get(i);
1057                            Xid xid = (Xid) loggedXids.get(i);
1058    
1059                            // Commit every resource that have been logged even if any of
1060                            // the commit resources fail. During recovery, the administrator
1061                            // will resolve any incomplete transactions.
1062    
1063                            if (TraceTm.jta.isDebugEnabled()) {
1064                                    TraceTm.jta.debug("xid=        " + xid);
1065                                    TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
1066                                    TraceTm.jta.debug("attempting commit with resource= " + res);
1067                            }
1068    
1069                            if (Current.getDefaultRecovery()) {
1070                                    int rmindex = 99; // Store 99, at recovery we will store the correct index
1071                                    byte[] resname = (byte[]) byteloggedResources.get(i);
1072                                    int resnamelength = resname.length;
1073    
1074                                    String resclassname = res.getClass().getName();
1075                                    int resclassnamelength = resclassname.length();
1076                                    int xidfi = xid.getFormatId();
1077                                    byte[] xidgti = xid.getGlobalTransactionId();
1078                                    byte[] xidbq = xid.getBranchQualifier();
1079                                    int rr2gtilen = xidgti.length;
1080                                    int rr2bqlen = xidbq.length;
1081    
1082                                    // formatid + : + gridlen + : + bqlen + : + xidgti + : + xidbq
1083    
1084                                    recoveryRecord2 = new byte[3 + 4 + 4 + resnamelength + 4 + resclassnamelength + 4 + 4 + rr2gtilen + 4
1085                                                    + rr2bqlen + 4];
1086    
1087                                    rr2 = ByteBuffer.wrap(recoveryRecord2);
1088    
1089                                    rr2.put(RT2);
1090                                    rr2.putInt(rmindex);
1091                                    rr2.putInt(resnamelength);
1092                                    rr2.put(resname);
1093                                    rr2.putInt(resclassnamelength);
1094                                    rr2.put(resclassname.getBytes());
1095                                    rr2.putInt(xidfi);
1096                                    rr2.putInt(xidgti.length);
1097                                    rr2.put(xidgti);
1098                                    rr2.putInt(xidbq.length);
1099                                    rr2.put(xidbq);
1100                            }
1101    
1102                            // commit resource
1103    
1104                            try {
1105                                    res.commit(myjavaxxid, false);
1106                                    // res.commit(xid, false);
1107    
1108                                    if (Current.getDefaultRecovery()) {
1109                                            rr2.putInt(Status.STATUS_COMMITTED);
1110                                            if (TraceTm.recovery.isDebugEnabled()) {
1111                                                    TraceTm.recovery.debug("Status Committed");
1112                                            }
1113                                    }
1114    
1115                                    commitnb++; // an XAresource was committed
1116                            } catch (XAException e) {
1117                                    switch (e.errorCode) {
1118                                    case XAException.XA_HEURHAZ:
1119                                    case XAException.XA_HEURCOM:
1120                                    case XAException.XA_HEURRB:
1121                                    case XAException.XA_HEURMIX:
1122                                            if (TraceTm.jta.isDebugEnabled()) {
1123                                                    TraceTm.jta.debug("Heuristic condition= " + e.getMessage());
1124                                            }
1125    
1126                                            if (Current.getDefaultRecovery()) {
1127                                                    rr2.putInt(Status.STATUS_UNKNOWN);
1128                                                    if (TraceTm.recovery.isDebugEnabled()) {
1129                                                            TraceTm.recovery.debug("Status Unknown");
1130                                                    }
1131                                            }
1132                                            break;
1133                                    case XAException.XAER_RMERR:
1134                                    case XAException.XAER_NOTA:
1135                                    case XAException.XAER_INVAL:
1136                                    case XAException.XAER_PROTO:
1137                                    case XAException.XAER_RMFAIL:
1138                                            if (TraceTm.jta.isDebugEnabled()) {
1139                                                    TraceTm.jta.debug("RM error= " + e.getMessage());
1140                                            }
1141    
1142                                            if (Current.getDefaultRecovery()) {
1143                                                    rr2.putInt(Status.STATUS_COMMITTING);
1144                                                    if (TraceTm.recovery.isDebugEnabled()) {
1145                                                            TraceTm.recovery.debug("Status Committing");
1146                                                    }
1147                                            }
1148                                            break;
1149                                    default:
1150                                            if (TraceTm.jta.isDebugEnabled()) {
1151                                                    TraceTm.jta.debug("Default error= " + e.getMessage());
1152                                            }
1153    
1154                                            if (Current.getDefaultRecovery()) {
1155                                                    rr2.putInt(Status.STATUS_ROLLEDBACK);
1156                                                    if (TraceTm.recovery.isDebugEnabled()) {
1157                                                            TraceTm.recovery.debug("Status Rolledback");
1158                                                    }
1159                                            }
1160                                    }
1161    
1162                                    String error = "Cannot send res.commit:" + e + " (error code = " + e.errorCode + ") --"
1163                                                    + e.getMessage();
1164                                    TraceTm.jotm.error("Got XAException from res.commit: " + error);
1165    
1166                                    successfulcommit = false;
1167                                    if (commitnb > 0) {
1168                                            heuristicnb++;
1169                                    }
1170                            }
1171    
1172                            if (Current.getDefaultRecovery()) {
1173                                    if (TraceTm.recovery.isDebugEnabled()) {
1174                                            TraceTm.recovery.debug("Prepare New RR2 to Recovery Buffer");
1175                                    }
1176                                    recoveryBuffer[i + 1] = rr2.array(); // First record (0) is always rr1
1177                            }
1178                    }
1179    
1180                    if (successfulcommit) {
1181                            // increment counter for management
1182                            Current.getCurrent().incrementCommitCounter();
1183                            // Everything's fine.
1184                            status = Status.STATUS_COMMITTED;
1185    
1186                            if (TraceTm.recovery.isDebugEnabled()) {
1187                                    TraceTm.recovery.debug("Status Committed");
1188                            }
1189    
1190                            if (Current.getDefaultRecovery()) {
1191                                    try {
1192                                            if (TraceTm.recovery.isDebugEnabled()) {
1193                                                    TraceTm.recovery.debug("Done howl log, all okay");
1194                                            }
1195    
1196                                            jotmDoneRecord[0] = JOTMDONE;
1197                                            TransactionRecoveryImpl.getTransactionRecovery().howlDoneLog(jotmDoneRecord, xaCommitTx);
1198                                    } catch (Exception f) {
1199                                            String howlerror = "Cannot howlDoneLog:" + f + "--" + f.getMessage();
1200                                            TraceTm.jotm.error("Got LogException from howlDoneLog: " + howlerror);
1201                                    }
1202                            }
1203    
1204                            doAfterCompletion();
1205                            log.forgetLog();
1206    
1207                            if (TraceTm.jta.isDebugEnabled()) {
1208                                    TraceTm.jta.debug("SubCoordinator.doCommit(): EXIT 0");
1209                            }
1210    
1211                            return 0;
1212                    }
1213    
1214                    /*****
1215                     * if (heuristicnb == 0) { // commits on all XAResources failed, just rollback // Transaction has been
1216                     * eventually rolled back status = Status.STATUS_ROLLEDBACK;
1217                     * 
1218                     * if (Current.getDefaultRecovery()) { try { jotmDoneRecord [0] = JOTMDONE;
1219                     * TransactionRecoveryImpl.getTransactionRecovery().howlDoneLog(jotmDoneRecord, xaCommitTx); } catch (Exception
1220                     * f) { String howlerror = "Cannot howlDoneLog" + f + "--" + f.getMessage();
1221                     * TraceTm.jotm.error("Got LogException from howlDoneLog: "+ howlerror); } }
1222                     * 
1223                     * doAfterCompletion(); log.forgetLog();
1224                     * 
1225                     * throw new TransactionRolledbackException(); }
1226                     *****/
1227    
1228                    // Log heuristics if errors
1229    
1230                    // if (heuristicnb != 0) { // some XAResource commits succeeded, others failed, heuristicmixed
1231    
1232                    if (Current.getDefaultRecovery()) {
1233                            try {
1234                                    if (TraceTm.recovery.isDebugEnabled()) {
1235                                            TraceTm.recovery.debug("Rewrite HowlCommitLog");
1236                                    }
1237                                    xaCommitTxRewrite = TransactionRecoveryImpl.getTransactionRecovery().howlCommitLog(recoveryBuffer);
1238                            } catch (Exception e) {
1239                                    // If we cannot write the Log, we cannot perform recovery, rollback transaction
1240                                    status = Status.STATUS_UNKNOWN;
1241                                    if (TraceTm.recovery.isDebugEnabled()) {
1242                                            TraceTm.recovery.debug("Status Unknown");
1243                                    }
1244    
1245                                    String howlerror = "Cannot howlCommitLog:" + e + " --" + e.getMessage();
1246                                    TraceTm.jotm.error("Got LogException from howlCommitLog: " + howlerror);
1247                                    xaCommitTx = null;
1248    
1249                                    doAfterCompletion();
1250                                    log.forgetLog();
1251    
1252                                    throw new JotmTransactionRolledbackException(e);
1253                            }
1254    
1255                            // Transaction state is unknown, now job for administrator
1256                            // status = Status.STATUS_UNKNOWN;
1257    
1258                            try {
1259                                    jotmDoneRecord[0] = JOTMDONE;
1260                                    TransactionRecoveryImpl.getTransactionRecovery().howlDoneLog(jotmDoneRecord, xaCommitTx);
1261                            } catch (Exception f) {
1262                                    String howlerror = "Cannot howlDoneLog" + f + "--" + f.getMessage();
1263                                    TraceTm.jotm.error("Got LogException from howlDoneLog: " + howlerror);
1264                            }
1265                    }
1266                    // }
1267    
1268                    status = Status.STATUS_UNKNOWN;
1269    
1270                    if (TraceTm.recovery.isDebugEnabled()) {
1271                            TraceTm.recovery.debug("Status Unknown");
1272                    }
1273    
1274                    doAfterCompletion();
1275    
1276                    if (TraceTm.jta.isDebugEnabled()) {
1277                            TraceTm.jta.debug("SubCoordinator.doCommit(): Exit -1");
1278                    }
1279    
1280                    return -1;
1281            }
1282    
1283            /**
1284             * 1PC (commit one phase)
1285             */
1286            private synchronized void doOnePhaseCommit() throws RemoteException {
1287                    if (TraceTm.jta.isDebugEnabled()) {
1288                            TraceTm.jta.debug("SubCoordinator.doOnePhaseCommit()");
1289                    }
1290    
1291                    // Only 1 resource: commit with onePhase=true
1292    
1293                    status = Status.STATUS_COMMITTING;
1294    
1295                    XAResource res = (XAResource) resourceList.elementAt(0);
1296                    javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(0);
1297    
1298                    if (TraceTm.jta.isDebugEnabled()) {
1299                            TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
1300                            TraceTm.jta.debug("one phase commit with resource= " + res);
1301                    }
1302    
1303                    try {
1304                            res.commit(myjavaxxid, true);
1305    
1306                            // increment counter for management
1307                            Current.getCurrent().incrementCommitCounter();
1308                            status = Status.STATUS_COMMITTED;
1309                    } catch (XAException e) {
1310                            status = Status.STATUS_UNKNOWN;
1311                            String error = "Cannot send res.commit:" + e + " (error code = " + e.errorCode + ") --" + e.getMessage();
1312                            TraceTm.jotm.error("Got XAException from res.commit: " + error);
1313    
1314                            if (e.errorCode == XAException.XA_RBROLLBACK) {
1315                                    throw new JotmTransactionRolledbackException("XAException:" + error, e);
1316                            }
1317                            throw new RemoteException("XAException:" + error);
1318                    } finally {
1319                            doAfterCompletion();
1320                    }
1321            }
1322    
1323            /**
1324             * Rollback every resource involved
1325             */
1326    
1327            private synchronized void doRollback() throws RemoteException {
1328                    if (TraceTm.jta.isDebugEnabled()) {
1329                            TraceTm.jta.debug("SubCoordinator.doRollback()");
1330                    }
1331    
1332                    status = Status.STATUS_ROLLEDBACK;
1333                    boolean heurroll = false;
1334                    String heuristic = null;
1335    
1336                    // roll back each resource
1337                    for (int i = 0; i < resourceList.size(); i++) {
1338    
1339                            XAResource res = (XAResource) resourceList.elementAt(i);
1340                            javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(i);
1341    
1342                            if (TraceTm.jta.isDebugEnabled()) {
1343                                    TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
1344                                    TraceTm.jta.debug("rolled back with resource= " + res);
1345                            }
1346    
1347                            // Rollback every resource that have been logged even if any of
1348                            // the rollback resources fail. During recovery, the administrator
1349                            // will resolve any incomplete transactions.
1350    
1351                            try {
1352                                    res.rollback(myjavaxxid);
1353                            } catch (XAException e) {
1354                                    switch (e.errorCode) {
1355                                    case XAException.XA_HEURHAZ:
1356                                    case XAException.XA_HEURCOM:
1357                                    case XAException.XA_HEURRB:
1358                                    case XAException.XA_HEURMIX:
1359                                            heuristic = "Heuristic condition= " + e.errorCode + "--" + e.getMessage();
1360                                            TraceTm.jta.warn(heuristic);
1361                                            heurroll = true;
1362                                            break;
1363                                    case XAException.XAER_RMERR:
1364                                    case XAException.XAER_NOTA:
1365                                    case XAException.XAER_INVAL:
1366                                    case XAException.XAER_PROTO:
1367                                    case XAException.XAER_RMFAIL:
1368                                            if (TraceTm.jta.isDebugEnabled()) {
1369                                                    TraceTm.jta.debug("RM error= " + e.errorCode + "--" + e.getMessage());
1370                                            }
1371                                            break;
1372                                    default:
1373                                            if (TraceTm.jta.isDebugEnabled()) {
1374                                                    TraceTm.jta.debug("Default error= " + e.errorCode + "--" + e.getMessage());
1375                                            }
1376                                    }
1377                            }
1378                    }
1379    
1380                    // raise Heuristic exception if XAResource returned heuristic
1381                    // no need to throw an exception for other XAResource errors,
1382                    // the resource (xid) has been rolled back anyway
1383    
1384                    if (heurroll) {
1385                            // pass the last heuristic error
1386                            throw new HeuristicRollback(heuristic);
1387                    }
1388    
1389                    // increment counter for management
1390    
1391                    Current.getCurrent().incrementRollbackCounter();
1392    
1393                    doAfterCompletion();
1394            }
1395    
1396            /**
1397             * before completion
1398             * 
1399             * @param boolean true if completion ok, false if rollback
1400             */
1401            private void doBeforeCompletion(boolean committing) {
1402                    if (TraceTm.jta.isDebugEnabled()) {
1403                            TraceTm.jta.debug("doBeforeCompletion committing= " + committing);
1404                    }
1405                    if (beforeCompletionDone) {
1406                            return;
1407                    }
1408    
1409                    // Unset the timer for this transaction, if any
1410                    tx.unsetTimer();
1411    
1412                    // For each synchro, send beforeCompletion (not if rollback)
1413    
1414                    if (committing && synchroList.size() > 0) {
1415                            // We must be in the correct transaction context
1416                            // See JTA spec. page 13 (3.3.2)
1417                            // because at beforeCompletion, the bean will write its cache
1418    
1419                            // Check the trivial case where we already have the correct tx context
1420    
1421                            Transaction mytx = null;
1422                            boolean suspended = false;
1423                            boolean resumed = false;
1424    
1425                            try {
1426                                    mytx = tm.getTransaction();
1427                            } catch (SystemException e) {
1428                                    if (TraceTm.jta.isDebugEnabled()) {
1429                                            String error = "Cannot get transaction:" + e + "--" + e.getMessage();
1430                                            TraceTm.jta.debug(error);
1431                                    }
1432                            }
1433    
1434                            // Suspend if another tx context
1435    
1436                            if (mytx != null && mytx.equals(tx) == false) {
1437                                    try {
1438                                            tm.suspend();
1439                                            suspended = true;
1440                                    } catch (SystemException e) {
1441                                            if (TraceTm.jta.isDebugEnabled()) {
1442                                                    String error = "Cannot suspend transaction:" + e + "--" + e.getMessage();
1443                                                    TraceTm.jta.debug(error);
1444                                            }
1445                                    }
1446                            }
1447    
1448                            // Resume the good tx context
1449    
1450                            if (mytx == null || suspended) {
1451                                    try {
1452                                            tm.resume(tx);
1453                                            resumed = true;
1454                                    } catch (SystemException e) {
1455                                            if (TraceTm.jta.isDebugEnabled()) {
1456                                                    String error = "Cannot resume transaction:" + e + "--" + e.getMessage();
1457                                                    TraceTm.jta.debug(error);
1458                                            }
1459                                    } catch (InvalidTransactionException e) {
1460                                            if (TraceTm.jta.isDebugEnabled()) {
1461                                                    String error = "Cannot resume transaction:" + e + "--" + e.getMessage();
1462                                                    TraceTm.jta.debug(error);
1463                                            }
1464                                    } catch (IllegalStateException e) {
1465                                            if (TraceTm.jta.isDebugEnabled()) {
1466                                                    String error = "Cannot resume transaction:" + e + "--" + e.getMessage();
1467                                                    TraceTm.jta.debug(error);
1468                                            }
1469                                    }
1470                            }
1471    
1472                            // Call the synchronizations
1473                            // beforeCompletion may set the TX rollbackonly if something goes wrong
1474    
1475                            if (TraceTm.jta.isDebugEnabled()) {
1476                                    TraceTm.jta.debug("sychronization list size= " + synchroList.size());
1477                            }
1478    
1479                            for (int i = 0; i < synchroList.size(); i++) {
1480                                    Synchronization sync = (Synchronization) synchroList.elementAt(i);
1481    
1482                                    if (TraceTm.jta.isDebugEnabled()) {
1483                                            TraceTm.jta.debug("Calling Synchro " + sync);
1484                                    }
1485    
1486                                    try {
1487                                            // This can register a new synchro, modifying synchroList!
1488                                            // TODO add a synchronized on list, and call beforeCompletion
1489                                            // outside the block.
1490                                            sync.beforeCompletion();
1491                                    } catch (RuntimeException e) {
1492                                            status = Status.STATUS_MARKED_ROLLBACK;
1493                                            rollbackCause = e;
1494                                            if (TraceTm.jta.isDebugEnabled()) {
1495                                                    String error = "Cannot sync.beforeCompletion:" + e + "--" + e.getMessage();
1496                                                    TraceTm.jta.debug(error);
1497                                            }
1498                                    }
1499                            }
1500    
1501                            // reset context as it was before
1502    
1503                            if (resumed) {
1504                                    try {
1505                                            tm.suspend();
1506                                    } catch (SystemException e) {
1507                                            if (TraceTm.jta.isDebugEnabled()) {
1508                                                    String error = "Cannot suspend transaction:" + e + "--" + e.getMessage();
1509                                                    TraceTm.jta.debug(error);
1510                                            }
1511                                    }
1512                            }
1513    
1514                            if (suspended) {
1515                                    try {
1516                                            tm.resume(mytx);
1517                                            resumed = true;
1518                                    } catch (SystemException e) {
1519                                            if (TraceTm.jta.isDebugEnabled()) {
1520                                                    String error = "Cannot resume transaction:" + e + "--" + e.getMessage();
1521                                                    TraceTm.jta.debug(error);
1522                                            }
1523                                    } catch (InvalidTransactionException e) {
1524                                            if (TraceTm.jta.isDebugEnabled()) {
1525                                                    String error = "Cannot resume transaction:" + e + "--" + e.getMessage();
1526                                                    TraceTm.jta.debug(error);
1527                                            }
1528                                    } catch (IllegalStateException e) {
1529                                            if (TraceTm.jta.isDebugEnabled()) {
1530                                                    String error = "Cannot resume transaction:" + e + "--" + e.getMessage();
1531                                                    TraceTm.jta.debug(error);
1532                                            }
1533                                    }
1534                            }
1535                    }
1536    
1537                    beforeCompletionDone = true;
1538            }
1539    
1540            /**
1541             * after completion
1542             */
1543            private void doAfterCompletion() {
1544                    if (TraceTm.jta.isDebugEnabled()) {
1545                            TraceTm.jta.debug("doAfterCompletion()");
1546                    }
1547    
1548                    // For each synchro, send afterCompletion
1549                    // / CompletedTransactionListener has been replaced by Synchronization
1550    
1551                    if (TraceTm.jta.isDebugEnabled()) {
1552                            TraceTm.jta.debug("sychronization list size= " + synchroList.size());
1553                    }
1554    
1555                    for (int i = 0; i < synchroList.size(); i++) {
1556                            Synchronization sync = (Synchronization) synchroList.elementAt(i);
1557    
1558                            if (TraceTm.jta.isDebugEnabled()) {
1559                                    TraceTm.jta.debug("Synchronization sync= " + sync);
1560                                    TraceTm.jta.debug("sync.afterCompletion status= " + StatusHelper.getStatusName(status));
1561                            }
1562    
1563                            sync.afterCompletion(status);
1564                    }
1565    
1566                    // Forget this transaction.
1567                    // LATER:
1568                    // - Should not forget it in case of heuristics (for recovery)
1569                    // - May be this could be deferred in case of retry from a client: use a timer.
1570                    Current.getCurrent().forgetTx(tx.getXid());
1571            }
1572    
1573            /**
1574             * return index in resourceList of this XAResource
1575             * 
1576             * @param xares
1577             *            the XAResource
1578             * @return index
1579             */
1580            public int getXaresIndex(XAResource xares) {
1581    
1582                    if (TraceTm.jta.isDebugEnabled()) {
1583                            TraceTm.jta.debug("getXaresIndex xares= " + xares);
1584                            TraceTm.jta.debug("resourceList.size= " + resourceList.size());
1585                    }
1586    
1587                    int xaresIndex = -1;
1588    
1589                    // first, search for an XAResource with the same object reference
1590                    if (TraceTm.jta.isDebugEnabled()) {
1591                            TraceTm.jta.debug("search xares with same obj ref");
1592                    }
1593    
1594                    for (int i = 0; i < resourceList.size(); i++) {
1595                            XAResource res = (XAResource) resourceList.elementAt(i);
1596    
1597                            if (TraceTm.jta.isDebugEnabled()) {
1598                                    TraceTm.jta.debug("res= " + res);
1599                            }
1600    
1601                            if (res.equals(xares)) {
1602                                    xaresIndex = i;
1603                                    break;
1604                            }
1605                    }
1606    
1607                    // if not found, search for a xares with the same RM
1608                    if (xaresIndex == -1) {
1609                            if (TraceTm.jta.isDebugEnabled()) {
1610                                    TraceTm.jta.debug("not found -> search for xares with same RM");
1611                            }
1612    
1613                            for (int i = 0; i < resourceList.size(); i++) {
1614                                    XAResource res = (XAResource) resourceList.elementAt(i);
1615    
1616                                    if (TraceTm.jta.isDebugEnabled()) {
1617                                            TraceTm.jta.debug("res= " + res);
1618                                    }
1619    
1620                                    try {
1621                                            if (res.isSameRM(xares)) {
1622                                                    xaresIndex = i;
1623                                                    break;
1624                                            }
1625                                    } catch (XAException e) {
1626                                            if (TraceTm.jta.isDebugEnabled()) {
1627                                                    String error = "res.isSameRm exception:" + e + "--" + e.getMessage();
1628                                                    TraceTm.jta.debug(error);
1629                                            }
1630                                    }
1631                            }
1632                    }
1633    
1634                    if (TraceTm.jta.isDebugEnabled()) {
1635                            TraceTm.jta.debug("xaresIndex= " + xaresIndex);
1636                    }
1637    
1638                    return xaresIndex;
1639            }
1640    
1641    }