View Javadoc

1   /*
2    * @(#) TransactionImpl.java
3    *
4    * JOTM: Java Open Transaction Manager
5    *
6    *
7    * This module was originally developed by
8    *
9    *  - Bull S.A. as part of the JOnAS application server code released in
10   *    July 1999 (www.bull.com)
11   *
12   * --------------------------------------------------------------------------
13   *  The original code and portions created by Bull SA are
14   *  Copyright (c) 1999 BULL SA
15   *  All rights reserved.
16   *
17   * Redistribution and use in source and binary forms, with or without
18   * modification, are permitted provided that the following conditions are met:
19   *
20   * -Redistributions of source code must retain the above copyright notice, this
21   * list of conditions and the following disclaimer.
22   *
23   * -Redistributions in binary form must reproduce the above copyright notice,
24   * this list of conditions and the following disclaimer in the documentation
25   * and/or other materials provided with the distribution.
26   *
27   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37   * POSSIBILITY OF SUCH DAMAGE.
38   *
39   * --------------------------------------------------------------------------
40   * Contributor(s):
41   * 01/11/06 Christophe Ney cney@batisseurs.com
42   *          Added ResourceManagerListener mechanism to remove ThreadData
43   *          dependency.
44   *
45   * 02/01/15 Dean Jennings - List  instead of Vector for enlistedXARes delistedXARes
46   *
47   * 02/06/18 Marek Prochazka - in timeoutExpired() removed code
48   *          preventing deadlock by rolling back transaction
49   * --------------------------------------------------------------------------
50   * $Id: TransactionImpl.java,v 1.51 2006-09-07 00:18:41 tonyortiz Exp $
51   * --------------------------------------------------------------------------
52   */
53  package org.objectweb.jotm;
54  
55  import java.rmi.NoSuchObjectException;
56  import java.rmi.RemoteException;
57  import java.util.ArrayList;
58  import java.util.Collections;
59  import java.util.Date;
60  import java.util.HashMap;
61  import java.util.List;
62  import java.util.Map;
63  
64  import javax.rmi.PortableRemoteObject;
65  import javax.transaction.HeuristicMixedException;
66  import javax.transaction.HeuristicRollbackException;
67  import javax.transaction.RollbackException;
68  import javax.transaction.Status;
69  import javax.transaction.Synchronization;
70  import javax.transaction.SystemException;
71  import javax.transaction.Transaction;
72  import javax.transaction.TransactionRolledbackException;
73  import javax.transaction.xa.XAException;
74  import javax.transaction.xa.XAResource;
75  
76  /**
77   * TransactionImpl is the implementation of the Transaction interface, defined in JTA specifications. This object is
78   * intended to be used by the EJBServer for transaction management. It is used indirectly by the UserTransaction
79   * implementation too, i.e. the Current object. The goal is to use the JTA interface to hide the JTM interface to the
80   * caller (EJBServer, Bean or Client).
81   */
82  
83  public class TransactionImpl implements Transaction, TimerEventListener {
84  
85  	// ------------------------------------------------------------------
86  	// Private data
87  	// ------------------------------------------------------------------
88  	private SubCoordinator subcoord = null;
89  	private TransactionContext myCtx = null;
90  	private Xid myXid = null;
91  	private boolean genXidhashcode = false;
92  	private boolean genXidtostring = false;
93  	private int myXidhashcode = 0;
94  	private String myXidtostring = null;
95  	private Date txDate = null;
96  	private TimerEvent timer = null; // keep this to unvalidate timer
97  	private RecoveryCoordinator recoveryCoord = null;
98  	// / store enlisted resources
99  	private List<XAResource> enlistedXARes = Collections.synchronizedList(new ArrayList<XAResource>());
100 	// / store suspended resources
101 	private List<XAResource> delistedXARes = null;
102 
103 	/**
104 	 * propagate context or not. No need to propagate Context when accessing TM for example TODO Add a synchro on this
105 	 * object.
106 	 */
107 	private boolean propagateCtx = true;
108 	private List<javax.transaction.xa.Xid> enlistedJavaxXid = Collections
109 			.synchronizedList(new ArrayList<javax.transaction.xa.Xid>());
110 	private Map<Object, Object> userResourceMap = null;
111 
112 	/**
113 	 * Actual Status is kept inside SubCoordinator. This one is used only when SubCoordinator does not exist.
114 	 */
115 	private int localstatus = Status.STATUS_ACTIVE;
116 
117 	private boolean toremove = false;
118 
119 	// ------------------------------------------------------------------
120 	// Constructors
121 	// ------------------------------------------------------------------
122 
123 	/**
124 	 * New transaction (begin).
125 	 * 
126 	 * @param xid
127 	 *            transaction Xid
128 	 * @param timeout
129 	 *            The value of the timeout in seconds.
130 	 * @throws SystemException
131 	 *             could not build Transaction Context
132 	 */
133 	public TransactionImpl(Xid xid, int timeout) throws SystemException {
134 		if (TraceTm.jta.isDebugEnabled()) {
135 			TraceTm.jta.debug("xid= " + xid);
136 			TraceTm.jta.debug("timeout= " + timeout);
137 		}
138 
139 		// Build a propagation context local (no ref to JTM yet)
140 		myXid = xid;
141 		myCtx = new InternalTransactionContext(timeout, null, xid);
142 	}
143 
144 	/**
145 	 * New Transaction for this thread (setPropagationContext)
146 	 * 
147 	 * @param pctx
148 	 *            propagation context
149 	 * 
150 	 */
151 	public TransactionImpl(TransactionContext pctx) {
152 
153 		if (pctx == null) {
154 			TraceTm.jotm.error("TransactionImpl: null PropagationContext");
155 			return;
156 		}
157 		myCtx = pctx;
158 		myXid = pctx.getXid();
159 
160 		// Make interposition in any case, to solve problems of memory leaks
161 		// and bad transaction count, in case no resource will be implied.
162 		// -> this make sure that commit will remove this object.
163 		try {
164 			makeSubCoord(true, true);
165 		} catch (RollbackException e) {
166 			toremove = true;
167 			TraceTm.jotm.debug("already rolled back");
168 			localstatus = Status.STATUS_ROLLEDBACK;
169 		} catch (SystemException e) {
170 			toremove = true;
171 			TraceTm.jotm.error("cannot make subcoordinator");
172 			localstatus = Status.STATUS_ROLLEDBACK;
173 		}
174 	}
175 
176 	// ------------------------------------------------------------------
177 	// Transaction Synchronization Registry implementation
178 	// ------------------------------------------------------------------
179 
180 	/**
181 	 * Save User Resource
182 	 * 
183 	 * @param key
184 	 *            object
185 	 * 
186 	 * @param value
187 	 *            object
188 	 * 
189 	 */
190 	public synchronized void putUserResource(Object key, Object value) {
191 
192 		if (userResourceMap == null) {
193 			userResourceMap = Collections.synchronizedMap(new HashMap<Object, Object>());
194 		}
195 		userResourceMap.put(key, value);
196 	}
197 
198 	/**
199 	 * Get User Resource
200 	 * 
201 	 * @return Object object
202 	 * 
203 	 * @param key
204 	 *            object
205 	 * 
206 	 */
207 	public synchronized Object getUserResource(Object key) {
208 
209 		if (userResourceMap == null) {
210 			return null;
211 		}
212 
213 		return userResourceMap.get(key);
214 	}
215 
216 	/**
217 	 * Register InterposedSynchronization
218 	 * 
219 	 * @param sync
220 	 *            synchronization
221 	 * @throws IllegalStateException
222 	 *             could not register synchronization
223 	 */
224 	public void registerInterposedSynchronization(Synchronization sync) throws IllegalStateException {
225 		try {
226 			registerSynchronization(sync);
227 		} catch (Exception e) {
228 			throw new IllegalStateException();
229 		}
230 	}
231 
232 	// ------------------------------------------------------------------
233 	// Transaction implementation
234 	// ------------------------------------------------------------------
235 
236 	/**
237 	 * Complete the transaction represented by this Transaction object The calling thread is not required to have the
238 	 * same transaction associated with the thread. (JTA 3.3.3)
239 	 * 
240 	 * @exception RollbackException
241 	 *                Thrown to indicate that the transaction has been rolled back rather than committed.
242 	 * 
243 	 * @exception HeuristicMixedException
244 	 *                Thrown to indicate that a heuristic decision was made and that some relevant updates have been
245 	 *                committed while others have been rolled back.
246 	 * 
247 	 * @exception HeuristicRollbackException
248 	 *                Thrown to indicate that a heuristic decision was made and that some relevant updates have been
249 	 *                rolled back.
250 	 * 
251 	 * @exception SecurityException
252 	 *                Thrown to indicate that the thread is not allowed to commit the transaction.
253 	 * 
254 	 * @exception IllegalStateException
255 	 *                Thrown if the current thread is not associated with a transaction.
256 	 * 
257 	 * @exception SystemException
258 	 *                Thrown if the transaction manager encounters an unexpected error condition
259 	 */
260 	@Override
261 	public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
262 			SecurityException, SystemException {
263 
264 		if (TraceTm.jta.isDebugEnabled()) {
265 			TraceTm.jta.debug("TransactionImpl.commit (tx= " + this + ")");
266 		}
267 
268 		// *** Distributed transaction.
269 		Terminator term = myCtx.getTerminator();
270 
271 		if (term != null) {
272 			// Commits the Transaction, with heuristic report
273 			try {
274 				propagateCtx = false;
275 				term.commit(true);
276 			} catch (TransactionRolledbackException e) {
277 				Current.getCurrent().forgetTx(getXid());
278 				if (TraceTm.jta.isDebugEnabled()) {
279 					TraceTm.jta.debug("Commit distributed transaction -> rolled back!");
280 				}
281 				localstatus = Status.STATUS_ROLLEDBACK;
282 				RollbackException ex = new RollbackException();
283 				ex.initCause(e);
284 				throw ex;
285 			} catch (RemoteException e) {
286 
287 				if (TraceTm.jta.isWarnEnabled()) {
288 					TraceTm.jta.warn("got a RemoteException", e);
289 				}
290 
291 				if (e.detail instanceof TransactionRolledbackException) {
292 					Current.getCurrent().forgetTx(getXid());
293 					if (TraceTm.jta.isDebugEnabled()) {
294 						TraceTm.jta.debug("Commit distributed transaction -> rolled back!");
295 					}
296 					localstatus = Status.STATUS_ROLLEDBACK;
297 					RollbackException ex = new RollbackException();
298 					ex.initCause(e.detail);
299 					throw ex;
300 				}
301 
302 				if (e.detail instanceof HeuristicMixed) {
303 					TraceTm.jotm.info("Commit distributed transaction -> Heuristic mixed!");
304 					throw new HeuristicMixedException();
305 				} else {
306 					SystemException ex = new SystemException("Unexpected RemoteException on commit:"
307 							+ e.detail.getMessage());
308 					ex.initCause(e.detail);
309 					throw ex;
310 				}
311 			} catch (Exception e) {
312 				TraceTm.jotm.error("Unexpected Exception on commit:", e);
313 				SystemException ex = new SystemException("Unexpected Exception on commit");
314 				ex.initCause(e);
315 				throw ex;
316 			} finally {
317 				propagateCtx = true;
318 				if (subcoord == null) {
319 					// if no coordinator, timer will not be unset by JTM.
320 					unsetTimer();
321 				}
322 			}
323 			Current.getCurrent().forgetTx(getXid());
324 			localstatus = Status.STATUS_COMMITTED;
325 			return;
326 		}
327 
328 		// *** Local transaction
329 		// commit_one_phase may raise remote exceptions. We must rethrow local exceptions.
330 
331 		if (subcoord != null) {
332 			try {
333 				subcoord.commit_one_phase();
334 			} catch (TransactionRolledbackException e) {
335 				if (TraceTm.jta.isDebugEnabled()) {
336 					TraceTm.jta.debug("Commit local transaction -> rolled back!");
337 				}
338 				Current.getCurrent().forgetTx(getXid());
339 				localstatus = Status.STATUS_ROLLEDBACK;
340 				RollbackException ex = new RollbackException();
341 				ex.initCause(e);
342 				throw ex;
343 			} catch (RemoteException e) {
344 				TraceTm.jotm.error("Unexpected Exception on commit_one_phase:", e);
345 				Current.getCurrent().forgetTx(getXid());
346 				localstatus = Status.STATUS_UNKNOWN;
347 				SystemException ex = new SystemException("Unexpected Exception on commit_one_phase");
348 				ex.initCause(e);
349 				throw ex;
350 			}
351 		} else {
352 			// if no coordinator, just unset the timer and release this object.
353 			unsetTimer();
354 			Current.getCurrent().forgetTx(getXid());
355 			localstatus = Status.STATUS_COMMITTED;
356 		}
357 	}
358 
359 	/**
360 	 * Delist the resource specified from the current transaction associated with the calling thread.
361 	 * 
362 	 * @param xares
363 	 *            The XAResource object representing the resource to delist
364 	 * 
365 	 * @param flag
366 	 *            One of the values of TMSUCCESS, TMSUSPEND, or TMFAIL.
367 	 * 
368 	 * @exception IllegalStateException
369 	 *                Thrown if the transaction in the target object is inactive.
370 	 * 
371 	 * @exception SystemException
372 	 *                Thrown if the transaction manager encounters an unexpected error condition
373 	 * 
374 	 * @return true if the dissociation of the Resource is successful; false otherwise.
375 	 */
376 	@Override
377 	public boolean delistResource(XAResource xares, int flag) throws IllegalStateException, SystemException {
378 
379 		if (TraceTm.jta.isDebugEnabled()) {
380 			TraceTm.jta.debug("TransactionImpl.delistResource");
381 			TraceTm.jta.debug("xares= " + xares + ", flag= " + flag);
382 		}
383 
384 		if (enlistedXARes == null) {
385 			if (TraceTm.jta.isDebugEnabled()) {
386 				TraceTm.jta.error("No XA resources enlisted by JOTM");
387 			}
388 			return false;
389 		}
390 
391 		// Verify that the XAResource to be delisted was enlisted earlier.
392 
393 		if (!enlistedXARes.contains(xares)) {
394 			if (TraceTm.jta.isDebugEnabled()) {
395 				TraceTm.jta.error("XAResouce " + xares + " not enlisted by JOTM");
396 			}
397 			return false;
398 		}
399 
400 		javax.transaction.xa.Xid javaxxid = subcoord.getJavaxXid(subcoord.getXaresIndex(xares));
401 
402 		if (!enlistedJavaxXid.contains(javaxxid)) {
403 			if (TraceTm.jta.isDebugEnabled()) {
404 				TraceTm.jta.error("XAResouce " + xares + " not enlisted by JOTM");
405 			}
406 			return false;
407 		}
408 
409 		int javaxxidindex = enlistedJavaxXid.indexOf(javaxxid);
410 		javax.transaction.xa.Xid myjavaxxid = enlistedJavaxXid.get(javaxxidindex);
411 
412 		if (TraceTm.jta.isDebugEnabled()) {
413 			TraceTm.jta.debug("delisted with resource= " + xares);
414 			TraceTm.jta.debug("end myjavaxxid= " + myjavaxxid);
415 		}
416 
417 		// Send the XA end to the XAResource
418 		try {
419 			xares.end(myjavaxxid, flag);
420 		} catch (XAException e) {
421 			String error = "Cannot send XA end:" + e + " (error code = " + e.errorCode + ") --" + e.getMessage();
422 			TraceTm.jotm.error(error);
423 			if (TraceTm.jta.isDebugEnabled()) {
424 				TraceTm.jotm.debug("xares.end= " + xares);
425 			}
426 			throw new SystemException(error);
427 		}
428 
429 		if (TraceTm.jta.isDebugEnabled()) {
430 			TraceTm.jta.debug("enlistedXAres.remove xares= " + xares);
431 		}
432 
433 		// / remove from enlisted list
434 		enlistedXARes.remove(xares);
435 		enlistedJavaxXid.remove(javaxxid);
436 		return true;
437 	}
438 
439 	/**
440 	 * Enlist the resource specified with the current transaction context of the calling thread
441 	 * 
442 	 * @param xares
443 	 *            The XAResource object representing the resource to enlist
444 	 * 
445 	 * @return <i>true</i> if the resource was enlisted successfully; otherwise false.
446 	 * 
447 	 * @exception RollbackException
448 	 *                Thrown to indicate that the transaction has been marked for rollback only.
449 	 * 
450 	 * @exception IllegalStateException
451 	 *                Thrown if the transaction in the target object is in prepared state or the transaction is
452 	 *                inactive.
453 	 * 
454 	 * @exception SystemException
455 	 *                Thrown if the transaction manager encounters an unexpected error condition
456 	 * 
457 	 */
458 	@Override
459 	public boolean enlistResource(XAResource xares) throws RollbackException, IllegalStateException, SystemException {
460 
461 		if (TraceTm.jta.isDebugEnabled()) {
462 			TraceTm.jta.debug("TransactionImpl.enlistResource");
463 			TraceTm.jta.debug("xares= " + xares);
464 		}
465 
466 		// Check trivial cases
467 		if (xares == null) {
468 			TraceTm.jotm.error("enlistResource: null argument");
469 			throw new SystemException("enlistResource: null argument");
470 		}
471 
472 		if (myCtx == null) {
473 			throw new SystemException("enlistResource: no Transactional Context");
474 		}
475 
476 		// make a subCoordinator object if not existing yet
477 		if (subcoord == null) {
478 			makeSubCoord(false, true);
479 			if (subcoord == null) {
480 				TraceTm.jotm.error("enlistResource: could not create subcoordinator");
481 				throw new SystemException("enlistResource: could not create subcoordinator");
482 			}
483 		}
484 
485 		boolean found;
486 
487 		try {
488 			found = subcoord.addResource(xares);
489 		} catch (IllegalStateException e) {
490 			throw new IllegalStateException("enlistResource: could not addResource " + xares);
491 		}
492 		// Transaction may have been set rollback only.
493 		// Enlist is done, but RollbackException will be thrown anyway.
494 
495 		// Send the XA start to the XAResource
496 		// A new Xid branch should be generated in case of new RM (if !found)
497 		// See JTA Specifications, page 12/13.
498 		int flag = found ? XAResource.TMJOIN : XAResource.TMNOFLAGS;
499 
500 		if ((delistedXARes != null) && delistedXARes.contains(xares)) {
501 			flag = XAResource.TMRESUME;
502 		}
503 
504 		Xid resXid = new XidImpl(getXid(), subcoord.getXaresIndex(xares));
505 		javax.transaction.xa.Xid javaxxid = new JavaXidImpl(resXid);
506 
507 		if (TraceTm.jta.isDebugEnabled()) {
508 			TraceTm.jta.debug("enlisted with resource= " + xares);
509 			TraceTm.jta.debug("start javaxxid= " + javaxxid);
510 		}
511 
512 		if (!found) {
513 			subcoord.addJavaxXid(javaxxid);
514 		}
515 
516 		try {
517 			xares.start(javaxxid, flag);
518 		} catch (XAException e) {
519 			String error = "Cannot send XA(" + xares + ") start:" + e + " (error code = " + e.errorCode + ") --"
520 					+ e.getMessage();
521 			TraceTm.jotm.error(error);
522 			throw new SystemException(error);
523 		}
524 
525 		if (!enlistedXARes.contains(xares)) {
526 			// / add to enlisted list
527 			enlistedXARes.add(xares);
528 			enlistedJavaxXid.add(javaxxid);
529 		}
530 
531 		int status = this.getStatus();
532 
533 		switch (status) {
534 		case Status.STATUS_ACTIVE:
535 		case Status.STATUS_PREPARING:
536 			break;
537 		case Status.STATUS_PREPARED:
538 			throw new IllegalStateException("Transaction already prepared.");
539 		case Status.STATUS_COMMITTING:
540 			// throw new IllegalStateException("Transaction already started committing.");
541 			break;
542 		case Status.STATUS_COMMITTED:
543 			throw new IllegalStateException("Transaction already committed.");
544 		case Status.STATUS_MARKED_ROLLBACK:
545 			throw new RollbackException("Transaction already marked for rollback");
546 		case Status.STATUS_ROLLING_BACK:
547 			throw new RollbackException("Transaction already started rolling back.");
548 		case Status.STATUS_ROLLEDBACK:
549 			throw new RollbackException("Transaction already rolled back.");
550 		case Status.STATUS_NO_TRANSACTION:
551 			throw new IllegalStateException("No current transaction.");
552 		case Status.STATUS_UNKNOWN:
553 			throw new IllegalStateException("Unknown transaction status");
554 		default:
555 			throw new IllegalStateException("Illegal transaction status: " + status);
556 		}
557 
558 		return true;
559 	}
560 
561 	/**
562 	 * delist all enlisted resources and move to suspended
563 	 */
564 	public void doDetach(int flag) throws SystemException {
565 		if (TraceTm.jta.isDebugEnabled()) {
566 			TraceTm.jta.debug("TransactionImpl.doDetach flag= " + XAResourceHelper.getFlagName(flag));
567 			TraceTm.jta.debug("number of enlisted= " + enlistedXARes.size());
568 		}
569 
570 		// always copy enlisted to suspended resource list
571 		// since jonas may resume the transaction in beforecompletion
572 		delistedXARes = new ArrayList<XAResource>(enlistedXARes);
573 		for (XAResource xar : delistedXARes) {
574 			delistResource(xar, flag);
575 		}
576 	}
577 
578 	/**
579 	 * enlist/clear all suspended resource
580 	 */
581 	public void doAttach(int flag) throws SystemException, RollbackException {
582 		if (TraceTm.jta.isDebugEnabled()) {
583 			TraceTm.jta.debug("TransactionImpl.doAttach flag= " + XAResourceHelper.getFlagName(flag));
584 			TraceTm.jta.debug("number of enlisted= " + enlistedXARes.size());
585 		}
586 
587 		boolean rollbackonenlist = false;
588 		RollbackException mye = null;
589 
590 		// we attach suspended transactions
591 
592 		if (flag == XAResource.TMRESUME) {
593 			// we may be calling resume from beforecompletion on transaction that are not suspended!
594 
595 			for (int i = 0; (delistedXARes != null) && (i < delistedXARes.size()); i++) {
596 
597 				try {
598 					enlistResource(delistedXARes.get(i));
599 				} catch (RollbackException e) {
600 					if (!rollbackonenlist) {
601 						rollbackonenlist = true;
602 						mye = e;
603 					}
604 				}
605 			}
606 		}
607 
608 		delistedXARes = null;
609 
610 		if (rollbackonenlist) {
611 			throw new RollbackException(mye.getMessage());
612 		}
613 	}
614 
615 	/**
616 	 * get a copy of the list of currently enlisted resource
617 	 */
618 	public List getEnlistedXAResource() {
619 		if (TraceTm.jta.isDebugEnabled()) {
620 			TraceTm.jta.debug("getEnlistedXAResource size= " + enlistedXARes.size());
621 		}
622 		return new ArrayList<XAResource>(enlistedXARes);
623 	}
624 
625 	/**
626 	 * Obtain the status of the transaction associated with the current thread.
627 	 * 
628 	 * @return The transaction status. If no transaction is associated with the current thread, this method returns the
629 	 *         Status.NoTransaction value.
630 	 * 
631 	 * @exception SystemException
632 	 *                Thrown if the transaction manager encounters an unexpected error condition
633 	 * 
634 	 */
635 	@Override
636 	public int getStatus() throws SystemException {
637 		if (TraceTm.jta.isDebugEnabled()) {
638 			TraceTm.jta.debug("TransactionImpl.getStatus()");
639 		}
640 
641 		// *** Distributed transaction
642 		Coordinator coord = myCtx.getCoordinator();
643 
644 		if (coord != null) {
645 			// Ask the transaction status to JTM
646 			int ret;
647 			try {
648 				propagateCtx = false;
649 				ret = coord.get_status();
650 			} catch (Exception e) {
651 				TraceTm.jotm.error("cannot reach JTM:", e);
652 				return Status.STATUS_NO_TRANSACTION;
653 			} finally {
654 				propagateCtx = true;
655 			}
656 			return ret;
657 		}
658 
659 		// *** Local transaction
660 		// The status is kept in the subcoordinator
661 		if (subcoord != null) {
662 			return subcoord.getStatus();
663 		} else {
664 			return localstatus;
665 		}
666 	}
667 
668 	/**
669 	 * Register a synchronization object for the transaction currently associated with the calling thread. The
670 	 * transction manager invokes the beforeCompletion method prior to starting the transaction commit process. After
671 	 * the transaction is completed, the transaction manager invokes the afterCompletion method.
672 	 * 
673 	 * @param sync
674 	 *            The javax.transaction.Synchronization object for the transaction associated with the target object
675 	 * 
676 	 * @exception RollbackException
677 	 *                Thrown to indicate that the transaction has been marked for rollback only.
678 	 * 
679 	 * @exception IllegalStateException
680 	 *                Thrown if the transaction in the target object is in prepared state or the transaction is
681 	 *                inactive.
682 	 * 
683 	 * @exception SystemException
684 	 *                Thrown if the transaction manager encounters an unexpected error condition
685 	 */
686 	@Override
687 	public void registerSynchronization(Synchronization sync) throws RollbackException, IllegalStateException,
688 			SystemException {
689 		if (TraceTm.jta.isDebugEnabled()) {
690 			TraceTm.jta.debug("Synchro=" + sync);
691 		}
692 
693 		// It's time to make the subcoordinator, if not existing yet.
694 		if (subcoord == null) {
695 			makeSubCoord(false, true);
696 		}
697 
698 		// Add Synchronization to the list.
699 		// may raise exceptions
700 		subcoord.addSynchronization(sync);
701 	}
702 
703 	/**
704 	 * Rollback the transaction represented by this Transaction object.
705 	 * 
706 	 * @exception IllegalStateException
707 	 *                Thrown if the transaction in the target object is in prepared state or the transaction is
708 	 *                inactive.
709 	 * 
710 	 * @exception SystemException
711 	 *                Thrown if the transaction manager encounters an unexpected error condition
712 	 * 
713 	 */
714 	@Override
715 	public void rollback() throws IllegalStateException, SystemException {
716 		if (TraceTm.jta.isDebugEnabled()) {
717 			TraceTm.jta.debug("TransactionImpl.rollback(tx= " + this + ")");
718 		}
719 
720 		// *** Distributed transaction.
721 		Terminator term = myCtx.getTerminator();
722 
723 		if (term != null) {
724 			// Rollback the Transaction
725 			try {
726 				propagateCtx = false;
727 				term.rollback();
728 			} catch (java.rmi.ServerException e) {
729 				// HeuristicCommit ????
730 				throw new IllegalStateException("Exception on rollback:" + e);
731 			} catch (Exception e) {
732 				Current.getCurrent().forgetTx(getXid());
733 				localstatus = Status.STATUS_UNKNOWN;
734 
735 				clearUserResourceMap();
736 
737 				throw new SystemException("Unexpected Exception on rollback");
738 			} finally {
739 				propagateCtx = true;
740 			}
741 
742 			if (subcoord == null) {
743 				// if no coordinator, timer will not be unset by JTM.
744 				unsetTimer();
745 			}
746 
747 			// release this object.
748 			Current.getCurrent().forgetTx(getXid());
749 			localstatus = Status.STATUS_ROLLEDBACK;
750 
751 			clearUserResourceMap();
752 
753 			return;
754 		}
755 
756 		// *** Local transaction.
757 		// if no coordinator, nothing to do.
758 
759 		if (subcoord != null) {
760 			try {
761 				subcoord.rollback();
762 			} catch (RemoteException e) {
763 				Current.getCurrent().forgetTx(getXid());
764 				localstatus = Status.STATUS_UNKNOWN;
765 
766 				clearUserResourceMap();
767 
768 				throw new IllegalStateException("Exception on rollback:" + e);
769 			}
770 
771 		} else {
772 			// if no coordinator, just unset the timer.
773 			unsetTimer();
774 		}
775 
776 		// release this object.
777 		Current.getCurrent().forgetTx(getXid());
778 		localstatus = Status.STATUS_ROLLEDBACK;
779 
780 		clearUserResourceMap();
781 	}
782 
783 	/**
784 	 * Prepare the transaction represented by this Transaction object.
785 	 * 
786 	 * @exception IllegalStateException
787 	 *                Thrown if the transaction in the target object is in prepared state or the transaction is
788 	 *                inactive.
789 	 * 
790 	 * @exception SystemException
791 	 *                Thrown if the transaction manager encounters an unexpected error condition
792 	 * 
793 	 * @return prepare status
794 	 */
795 	public int prepare() throws IllegalStateException, SystemException {
796 		if (TraceTm.jta.isDebugEnabled()) {
797 			TraceTm.jta.debug("TransactionImpl.prepare(tx= " + this + ")");
798 		}
799 
800 		int ret = 0;
801 		if (subcoord != null) {
802 			try {
803 				ret = subcoord.prepare();
804 			} catch (RemoteException e) {
805 				TraceTm.jotm.error("Unexpected Exception on prepare:", e);
806 				throw new SystemException("Unexpected Exception on prepare");
807 			}
808 		}
809 		return ret;
810 	}
811 
812 	/**
813 	 * Modify the transaction associated with the current thread such that the only possible outcome of the transaction
814 	 * is to roll back the transaction.
815 	 * 
816 	 * @exception IllegalStateException
817 	 *                Thrown if the current thread is not associated with any transaction.
818 	 * 
819 	 * @exception SystemException
820 	 *                Thrown if the transaction manager encounters an unexpected error condition
821 	 * 
822 	 */
823 	@Override
824 	public void setRollbackOnly() throws IllegalStateException, SystemException {
825 		if (TraceTm.jta.isDebugEnabled()) {
826 			TraceTm.jta.debug("Tx=" + this);
827 		}
828 
829 		Coordinator coord = myCtx.getCoordinator();
830 
831 		if (coord != null) {
832 			// Distributed transaction
833 			try {
834 				propagateCtx = false;
835 				coord.rollback_only();
836 			} catch (RemoteException e) {
837 				TraceTm.jotm.error("Cannot perform coordinator rollback only", e);
838 			} finally {
839 				propagateCtx = true;
840 			}
841 		}
842 
843 		// perform this even for distributed transactions:
844 		// resolves bugs 300077, 300078
845 		if (subcoord == null) {
846 			// make a subCoordinator object if not existing yet
847 			try {
848 				makeSubCoord(false, false);
849 			} catch (RollbackException e) {
850 				TraceTm.jotm.debug("already rolled back");
851 				return;
852 			}
853 		}
854 		subcoord.setRollbackOnly();
855 	}
856 
857 	// ------------------------------------------------------------------
858 	// TimerEventListener implementation
859 	// ------------------------------------------------------------------
860 
861 	/**
862 	 * timeout for that transaction has expired
863 	 */
864 
865 	@Override
866 	public void timeoutExpired(Object arg) {
867 		if (TraceTm.jta.isDebugEnabled()) {
868 			TraceTm.jta.debug("TransactionImpl.timeoutExpired");
869 		}
870 
871 		// increment counter for management
872 		Current.getCurrent().incrementExpiredCounter();
873 
874 		// make the subcoordinator object, if not existing yet.
875 
876 		if (subcoord == null) {
877 			// if this is a proxy, just forget this object. The JTM will
878 			// rollback transaction with its own timer.
879 			Terminator term = myCtx.getTerminator();
880 
881 			if (term != null) {
882 				if (TraceTm.jta.isDebugEnabled()) {
883 					TraceTm.jta.debug("forget tx (tx=" + this + ")");
884 				}
885 				Current.getCurrent().forgetTx(getXid());
886 				localstatus = Status.STATUS_ROLLEDBACK;
887 				return;
888 			}
889 			try {
890 				makeSubCoord(false, false);
891 			} catch (RollbackException e) {
892 				TraceTm.jotm.debug("already rolled back");
893 				localstatus = Status.STATUS_ROLLEDBACK;
894 				return;
895 			} catch (SystemException e) {
896 				TraceTm.jotm.error("cannot make subcoordinator");
897 				localstatus = Status.STATUS_ROLLEDBACK;
898 				return;
899 			}
900 		}
901 
902 		// Try to set it "rollback only"
903 		// avoids a rollback while SQL requests are in progress
904 		if (TraceTm.jta.isDebugEnabled()) {
905 			TraceTm.jta.debug("set rollback only (tx=" + this + ")");
906 		}
907 
908 		try {
909 			subcoord.setRollbackOnly();
910 		} catch (Exception e) {
911 			TraceTm.jotm.error("cannot rollbackonly:" + e);
912 		}
913 	}
914 
915 	// ------------------------------------------------------------------
916 	// This object is used as an HashTable index
917 	// ------------------------------------------------------------------
918 
919 	/**
920 	 * return true if objects are identical
921 	 */
922 	@Override
923 	public boolean equals(Object obj2) {
924 		if (obj2 instanceof TransactionImpl) {
925 			TransactionImpl tx2 = (TransactionImpl) obj2;
926 
927 			// trivial case
928 			if (tx2 == this) {
929 				return true;
930 			}
931 
932 			// compare otids
933 			return getXid().equals(tx2.getXid());
934 		} else {
935 			return false;
936 		}
937 	}
938 
939 	/**
940 	 * return a hashcode value for this object
941 	 */
942 	@Override
943 	public int hashCode() {
944 		if (!genXidhashcode) {
945 			genXidhashcode = true;
946 			myXidhashcode = getXid().hashCode();
947 		}
948 
949 		return myXidhashcode;
950 	}
951 
952 	// ------------------------------------------------------------------
953 	// Other methods
954 	// ------------------------------------------------------------------
955 
956 	/**
957 	 * string form
958 	 */
959 	@Override
960 	public String toString() {
961 		if (!genXidtostring) {
962 			genXidtostring = true;
963 			myXidtostring = getXid().toString();
964 		}
965 		return myXidtostring;
966 	}
967 
968 	/**
969 	 * Return associated PropagationContext Used for implicit Context propagation.
970 	 * 
971 	 * @param hold
972 	 *            true if must increment the count to hold the object (not used!)
973 	 * @return PropagationContext associated with the transaction.
974 	 */
975 	public synchronized TransactionContext getPropagationContext(boolean hold) {
976 		if (propagateCtx) {
977 			return myCtx;
978 		} else {
979 			return null;
980 		}
981 	}
982 
983 	/**
984 	 * set a timer for the transaction
985 	 * 
986 	 * @param timer
987 	 *            the timer event to set
988 	 */
989 	public void setTimer(TimerEvent timer) {
990 		if (TraceTm.jta.isDebugEnabled()) {
991 			TraceTm.jta.debug("set timer for tx (timer=" + timer + ", tx=" + this + ")");
992 		}
993 		this.timer = timer;
994 	}
995 
996 	/**
997 	 * unset the timer
998 	 */
999 	public void unsetTimer() {
1000 		if (TraceTm.jta.isDebugEnabled()) {
1001 			TraceTm.jta.debug("unset timer for tx (timer=" + timer + ", tx=" + this + ")");
1002 		}
1003 		if (timer != null) {
1004 			timer.unset();
1005 			timer = null;
1006 		}
1007 	}
1008 
1009 	/**
1010 	 * set the date time stamp for the transaction
1011 	 * 
1012 	 * @param date
1013 	 *            the Date to set for the transaction
1014 	 */
1015 	public void setTxDate(Date date) {
1016 		if (TraceTm.jta.isDebugEnabled()) {
1017 			TraceTm.jta.debug("set date for tx (data=" + date + ", tx=" + this + ")");
1018 		}
1019 		txDate = (Date) date.clone();
1020 	}
1021 
1022 	/**
1023 	 * get the date time stamp for the transaction
1024 	 * 
1025 	 * @return the timestamp
1026 	 */
1027 	public Date getTxDate() {
1028 		if (TraceTm.jta.isDebugEnabled()) {
1029 			TraceTm.jta.debug("get date for tx (date=" + txDate + ", tx=" + this + ")");
1030 		}
1031 		return (Date) txDate.clone();
1032 	}
1033 
1034 	/**
1035 	 * update the propagation context We should be inside the reply of a request involved in a tx here!
1036 	 * 
1037 	 * @param pctx
1038 	 *            propagation context
1039 	 */
1040 	public synchronized void updatePropagationContext(TransactionContext pctx) {
1041 		if (TraceTm.jta.isDebugEnabled()) {
1042 			TraceTm.jta.debug("TransactionImpl.updatePropagationContext");
1043 		}
1044 
1045 		Coordinator remoteCoord = pctx.getCoordinator();
1046 
1047 		if (remoteCoord == null && myCtx.getCoordinator() != null) {
1048 			TraceTm.jotm.error("setPropagationContext: Bad Coordinator");
1049 			TraceTm.jotm.error("remoteCoord = " + remoteCoord);
1050 			TraceTm.jotm.error("myCtx.getCoordinator()= " + myCtx.getCoordinator());
1051 			return;
1052 		}
1053 
1054 		// Interpose subCoordinator if newly distributed Tx
1055 		if (remoteCoord != null && myCtx.getCoordinator() == null) {
1056 			myCtx.setCoordinator(pctx.getCoordinator());
1057 
1058 			if (subcoord != null) {
1059 				// register the subCoordinator as a Resource.
1060 				TraceTm.jta.debug("register the subCoordinator as a Resource");
1061 				try {
1062 					propagateCtx = false;
1063 					recoveryCoord = remoteCoord.register_resource(subcoord);
1064 				} catch (RemoteException e) {
1065 					TraceTm.jotm.warn("Cannot make interposition :" + e.getCause());
1066 					return;
1067 				} finally {
1068 					propagateCtx = true;
1069 				}
1070 			}
1071 		}
1072 
1073 		if (pctx.getTerminator() != null) {
1074 			myCtx.setTerminator(pctx.getTerminator());
1075 		}
1076 	}
1077 
1078 	/**
1079 	 * Get the Xid of the transaction
1080 	 * 
1081 	 * @return the Xid
1082 	 */
1083 	public Xid getXid() {
1084 		return myXid;
1085 	}
1086 
1087 	/**
1088 	 * make a SubCoordinator for this Transaction object
1089 	 * 
1090 	 * @param interpose
1091 	 *            Make interposition if not already done.
1092 	 * @param active
1093 	 *            transaction still active.
1094 	 */
1095 	private void makeSubCoord(boolean interpose, boolean active) throws RollbackException, SystemException {
1096 		if (TraceTm.jta.isDebugEnabled()) {
1097 			TraceTm.jta.debug("make subcoordinator");
1098 		}
1099 
1100 		// Build the SubCoordinator object
1101 		try {
1102 			subcoord = new SubCoordinator(this, getXid());
1103 		} catch (RemoteException e) {
1104 			// should never go here.
1105 			TraceTm.jotm.error("new SubCoordinator raised exception: ", e);
1106 			return;
1107 		}
1108 		if (!active) {
1109 			// Just create the subCoordinator to store some state locally.
1110 			// Any attempt to register will fail if transaction is rolled back.
1111 			return;
1112 		}
1113 
1114 		// If interposition must be done: do it now!
1115 		// Each time we have a remoteCoord + a subCoord, we must interpose.
1116 		Coordinator remoteCoord = myCtx.getCoordinator();
1117 
1118 		// First of all, create the Control object on JTM
1119 		// if it was not created before, if interpose flag is set.
1120 		if (interpose && remoteCoord == null) {
1121 			try {
1122 				if (TraceTm.jta.isDebugEnabled()) {
1123 					TraceTm.jta.debug("Creating a remote Control on JTM for a distributed transaction");
1124 				}
1125 				propagateCtx = false;
1126 				// Control coord = Current.getJTM().create(myCtx.getTimeout());
1127 				// Control coord = Current.getJTM().create(myCtx.getTimeout(), getXid());
1128 				Control coord = Current.getJTM().recreate(myCtx);
1129 				remoteCoord = (Coordinator) javax.rmi.PortableRemoteObject.narrow(coord, Coordinator.class);
1130 
1131 			} catch (RemoteException e) {
1132 				TraceTm.jotm.error("Cannot create distributed transaction:", e);
1133 				cleanup();
1134 				return;
1135 			} finally {
1136 				propagateCtx = true;
1137 			}
1138 
1139 			myCtx.setCoordinator(remoteCoord);
1140 
1141 			// fix for transaction context propagation with
1142 			// the Jeremie protocol
1143 
1144 			if (myCtx.getTerminator() == null) {
1145 				myCtx.setTerminator((Terminator) remoteCoord);
1146 			}
1147 		}
1148 
1149 		// Achieve interposition if not already done:
1150 		// - register the subCoordinator as a Resource.
1151 		if (remoteCoord != null && recoveryCoord == null) {
1152 			try {
1153 				propagateCtx = false;
1154 				recoveryCoord = remoteCoord.register_resource(subcoord);
1155 			} catch (RemoteException e) {
1156 				// If cannot be registered, destroy it.
1157 				// transaction status will be only local.
1158 				cleanup();
1159 				if (e.getCause() instanceof TransactionRolledbackException) {
1160 					TraceTm.jotm.warn("Cannot Make Interposition: rolled back occured");
1161 					throw new RollbackException("Cannot Make Interposition");
1162 				} else {
1163 					TraceTm.jotm.warn("Cannot make Interposition:" + e.getCause());
1164 					throw new SystemException("Cannot Make Interposition");
1165 				}
1166 			} finally {
1167 				propagateCtx = true;
1168 			}
1169 		}
1170 		// increment counter for management
1171 		Current.getCurrent().incrementBeginCounter();
1172 	}
1173 
1174 	public boolean toRemove() {
1175 		return toremove;
1176 	}
1177 
1178 	public void cleanup() {
1179 		if (subcoord != null) {
1180 			TraceTm.jta.debug("unexport SubCoordinator");
1181 			try {
1182 				PortableRemoteObject.unexportObject(subcoord);
1183 			} catch (NoSuchObjectException e) {
1184 				TraceTm.jta.debug("Cannot unexport subcoord:" + e);
1185 			}
1186 			subcoord = null;
1187 		}
1188 	}
1189 
1190 	/**
1191 	 * clear userResourceMap
1192 	 */
1193 	private synchronized void clearUserResourceMap() {
1194 
1195 		if ((userResourceMap != null) && !(userResourceMap.isEmpty())) {
1196 			userResourceMap.clear();
1197 			userResourceMap = null;
1198 		}
1199 	}
1200 
1201 }