View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.objectweb.jotm;
17  
18  import java.nio.ByteBuffer;
19  import java.rmi.RemoteException;
20  import java.util.List;
21  import java.util.Vector;
22  
23  import javax.rmi.PortableRemoteObject;
24  import javax.transaction.InvalidTransactionException;
25  import javax.transaction.RollbackException;
26  import javax.transaction.Status;
27  import javax.transaction.Synchronization;
28  import javax.transaction.SystemException;
29  import javax.transaction.Transaction;
30  import javax.transaction.TransactionManager;
31  import javax.transaction.TransactionRolledbackException;
32  import javax.transaction.xa.XAException;
33  import javax.transaction.xa.XAResource;
34  
35  import org.objectweb.howl.log.xa.XACommittingTx;
36  
37  /**
38   * Log associated to this transaction coordinator
39   */
40  class SLog {
41  
42  	private Vector loggedResources = new Vector();
43  	private Vector loggedByteResources = new Vector();
44  
45  	private Vector loggedXids = new Vector();
46  
47  	private Vector loggedJavaxXids = new Vector();
48  
49  	int decision;
50  	static final int DECISION_TO_COMMIT = 1;
51  	static final int DECISION_TO_ROLLBACK = 2;
52  
53  	public void addResource(XAResource res, Xid xid, javax.transaction.xa.Xid javaxxid) {
54  		if (TraceTm.jta.isDebugEnabled()) {
55  			TraceTm.jta.debug("res= " + res);
56  			TraceTm.jta.debug("xid= " + xid);
57  			TraceTm.jta.debug("javaxxid= " + javaxxid);
58  		}
59  		loggedResources.addElement(res);
60  		loggedByteResources.addElement(res.toString().getBytes());
61  		loggedXids.addElement(xid);
62  		loggedJavaxXids.addElement(javaxxid);
63  	}
64  
65  	/**
66  	 * @return a List of logged Resources
67  	 */
68  	public List getLoggedResources() {
69  		if (TraceTm.jta.isDebugEnabled()) {
70  			TraceTm.jta.debug("logged resources=" + loggedResources);
71  		}
72  		return loggedResources;
73  	}
74  
75  	public List getByteLoggedResources() {
76  		if (TraceTm.jta.isDebugEnabled()) {
77  			TraceTm.jta.debug("logged resources=" + loggedResources);
78  		}
79  		return loggedByteResources;
80  	}
81  
82  	public List getLoggedXids() {
83  		return loggedXids;
84  	}
85  
86  	public List getLoggedJavaxXids() {
87  		return loggedJavaxXids;
88  	}
89  
90  	public void flushLog(int decide) {
91  		if (TraceTm.jta.isDebugEnabled()) {
92  			TraceTm.jta.debug("decide=" + decide);
93  		}
94  		decision = decide;
95  
96  		// XXX serialize log on disk
97  	}
98  
99  	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 }