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