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 }