1 /*
2 * JBoss, the OpenSource J2EE webOS
3 *
4 * Distributable under LGPL license.
5 * See terms of license at gnu.org.
6 */
7 package org.jboss.tm;
8
9 import org.jboss.logging.Logger;
10 import org.jboss.util.timeout.Timeout;
11 import org.jboss.util.timeout.TimeoutFactory;
12 import org.jboss.util.timeout.TimeoutTarget;
13
14 import javax.transaction.HeuristicMixedException;
15 import javax.transaction.HeuristicRollbackException;
16 import javax.transaction.RollbackException;
17 import javax.transaction.Status;
18 import javax.transaction.Synchronization;
19 import javax.transaction.SystemException;
20 import javax.transaction.Transaction;
21 import javax.transaction.xa.XAException;
22 import javax.transaction.xa.XAResource;
23 import javax.transaction.xa.Xid;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.ArrayList;
28
29
30 /**
31 * Our <code>Transaction</code> implementation.
32 *
33 * @see TxManager
34 *
35 * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Öberg</a>
36 * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a>
37 * @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
38 * @author <a href="mailto:toby.allsopp@peace.com">Toby Allsopp</a>
39 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
40 * @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
41 * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
42 * @version $Revision: 1.5.2.16 $
43 */
44 class TransactionImpl
45 implements Transaction, TimeoutTarget
46 {
47 // Constants -----------------------------------------------------
48
49 /**
50 * Code meaning "no heuristics seen",
51 * must not be XAException.XA_HEURxxx
52 */
53 private static final int HEUR_NONE = XAException.XA_RETRY;
54
55 // Resource states
56 private final static int RS_NEW = 0; // not yet enlisted
57 private final static int RS_ENLISTED = 1; // enlisted
58 private final static int RS_SUSPENDED = 2; // suspended
59 private final static int RS_ENDED = 3; // not associated
60 private final static int RS_VOTE_READONLY = 4; // voted read-only
61 private final static int RS_VOTE_OK = 5; // voted ok
62 private final static int RS_FORGOT = 6; // RM has forgotten
63
64
65 // Attributes ----------------------------------------------------
66
67 /** Class logger, we don't want a new logger with every transaction. */
68 private static Logger log = Logger.getLogger(TransactionImpl.class);
69
70 /** True if trace messages should be logged. */
71 private boolean trace = log.isTraceEnabled();
72
73 /** The ID of this transaction. */
74 private Xid xid;
75
76 private ArrayList threads = new ArrayList(1);
77
78 private HashMap transactionLocalMap = new HashMap();
79
80 private Throwable cause;
81
82 /**
83 * The global ID of this transaction.
84 * This is used as a transaction propagation context, and in the
85 * TxManager for mapping transaction IDs to transactions.
86 */
87 private GlobalId globalId;
88
89 /**
90 * The synchronizations to call back.
91 */
92 private Synchronization[] sync = new Synchronization[3];
93
94 /**
95 * Size of allocated synchronization array.
96 */
97 private int syncAllocSize = 3;
98
99 /**
100 * Count of synchronizations for this transaction.
101 */
102 private int syncCount = 0;
103
104 /**
105 * A list of the XARessources that have participated in this transaction.
106 */
107 private XAResource[] resources = new XAResource[3];
108
109 /**
110 * The state of the resources.
111 */
112 private int[] resourceState = new int[3];
113
114 /**
115 * Index of the first XAResource representing the same resource manager,
116 * or <code>-1</code> if this XAResource is the first XAResource in this
117 * transaction that represents its resource manager.
118 */
119 private int[] resourceSameRM = new int[3];
120
121 /**
122 * A list of the XARessources that have participated in this transaction.
123 */
124 private Xid[] resourceXids = new Xid[3];
125
126 /**
127 * Size of allocated resource arrays.
128 */
129 private int resourceAllocSize = 3;
130
131 /**
132 * Count of resources that have participated in this transaction.
133 * This contains a count of all XAResources, not a count of distinct
134 * resource managers.
135 * It is the length of resources and other such arrays.
136 */
137 private int resourceCount = 0;
138
139 /**
140 * Flags that it is too late to enlist new resources.
141 */
142 private boolean resourcesEnded = false;
143
144 /**
145 * Last branch id used.
146 */
147 private long lastBranchId = 0;
148
149 /**
150 * Status of this transaction.
151 */
152 private int status;
153
154 /**
155 * The heuristics status of this transaction.
156 */
157 private int heuristicCode = HEUR_NONE;
158
159 /**
160 * The time when this transaction was started.
161 */
162 private long start;
163
164 /**
165 * The timeout handle for this transaction.
166 */
167 private Timeout timeout;
168
169 /**
170 * Timeout in millisecs
171 */
172 private long timeoutPeriod;
173 /**
174 * Mutex for thread-safety. This should only be changed in the
175 * <code>lock()</code> and <code>unlock()</code> methods.
176 */
177 private boolean locked = false;
178
179 /**
180 * Flags that we are done with this transaction and that it can be reused.
181 */
182 private boolean done = false;
183
184 // Static --------------------------------------------------------
185
186 /**
187 * Factory for Xid instances of specified class.
188 * This is set from the <code>TransactionManagerService</code>
189 * MBean.
190 */
191 static XidFactoryMBean xidFactory;
192
193 static TransactionManagerService txManagerService;
194
195 /**
196 * This static code is only present for testing purposes so a
197 * tm can be usable without a lot of setup.
198 *
199 */
200 static void defaultXidFactory()
201 {
202 if (xidFactory == null)
203 {
204 xidFactory = new XidFactory();
205 } // end of if ()
206 }
207
208
209 // Constructors --------------------------------------------------
210
211 TransactionImpl(long timeout)
212 {
213 xid = xidFactory.newXid();
214 globalId = new GlobalId(xid);
215
216 status = Status.STATUS_ACTIVE;
217
218 start = System.currentTimeMillis();
219 this.timeout = TimeoutFactory.createTimeout(start+timeout, this);
220 this.timeoutPeriod = timeout;
221 if (trace)
222 log.trace("Created new instance for tx=" + toString());
223
224 }
225
226 // Implements TimeoutTarget --------------------------------------
227
228 /**
229 * Called when our timeout expires.
230 */
231 public void timedOut(Timeout timeout)
232 {
233 try
234 {
235 lock();
236
237 log.warn("Transaction " + toString() + " timed out." +
238 " status=" + getStringStatus(status));
239
240 if (this.timeout == null)
241 return; // Don't race with timeout cancellation.
242 this.timeout = null;
243
244 switch (status)
245 {
246 case Status.STATUS_ROLLEDBACK:
247 case Status.STATUS_COMMITTED:
248 case Status.STATUS_NO_TRANSACTION:
249 return; // Transaction done.
250
251 case Status.STATUS_ROLLING_BACK:
252 return; // Will be done shortly.
253
254 case Status.STATUS_COMMITTING:
255 // This is _very_ bad:
256 // We are in the second commit phase, and have decided
257 // to commit, but now we get a timeout and should rollback.
258 // So we end up with a mixed decision.
259 gotHeuristic(-1, XAException.XA_HEURMIX);
260 status = Status.STATUS_MARKED_ROLLBACK;
261 return; // commit will fail
262
263 case Status.STATUS_PREPARED:
264 // This is bad:
265 // We are done with the first phase, and are persistifying
266 // our decision. Fortunately this case is currently never
267 // hit, as we do not release the lock between the two phases.
268 case Status.STATUS_ACTIVE:
269 status = Status.STATUS_MARKED_ROLLBACK;
270 // fall through..
271 case Status.STATUS_MARKED_ROLLBACK:
272 // don't rollback for now, this messes up with the TxInterceptor.
273 interruptThreads();
274 return;
275
276 case Status.STATUS_PREPARING:
277 status = Status.STATUS_MARKED_ROLLBACK;
278 return; // commit will fail
279
280 default:
281 log.warn("Unknown status at timeout, tx=" + toString());
282 return;
283 }
284 } finally
285 {
286 unlock();
287 }
288 }
289
290 // Implements Transaction ----------------------------------------
291
292 public void commit()
293 throws RollbackException,
294 HeuristicMixedException,
295 HeuristicRollbackException,
296 java.lang.SecurityException,
297 java.lang.IllegalStateException,
298 SystemException
299 {
300 try
301 {
302 lock();
303
304 if (trace)
305 {
306 log.trace("Committing, tx=" + this +
307 ", status=" + getStringStatus(status));
308 }
309
310 switch (status)
311 {
312 case Status.STATUS_PREPARING:
313 throw new IllegalStateException("Already started preparing.");
314 case Status.STATUS_PREPARED:
315 throw new IllegalStateException("Already prepared.");
316 case Status.STATUS_ROLLING_BACK:
317 throw new IllegalStateException("Already started rolling back.");
318 case Status.STATUS_ROLLEDBACK:
319 instanceDone();
320 checkHeuristics();
321 throw new IllegalStateException("Already rolled back.");
322 case Status.STATUS_COMMITTING:
323 throw new IllegalStateException("Already started committing.");
324 case Status.STATUS_COMMITTED:
325 instanceDone();
326 checkHeuristics();
327 throw new IllegalStateException("Already committed.");
328 case Status.STATUS_NO_TRANSACTION:
329 throw new IllegalStateException("No transaction.");
330 case Status.STATUS_UNKNOWN:
331 throw new IllegalStateException("Unknown state");
332 case Status.STATUS_MARKED_ROLLBACK:
333 doBeforeCompletion();
334 endResources();
335 rollbackResources();
336 doAfterCompletion();
337 cancelTimeout();
338 instanceDone();
339 checkHeuristics();
340 throw new RollbackException("Already marked for rollback");
341 case Status.STATUS_ACTIVE:
342 break;
343 default:
344 throw new IllegalStateException("Illegal status: " + status);
345 }
346
347 doBeforeCompletion();
348
349 if (trace)
350 {
351 log.trace("Before completion done, tx=" + this +
352 ", status=" + getStringStatus(status));
353 }
354
355 endResources();
356
357 if (status == Status.STATUS_ACTIVE)
358 {
359 if (resourceCount == 0)
360 {
361 // Zero phase commit is really fast ;-)
362 if (trace)
363 {
364 log.trace("Zero phase commit: No resources.");
365 }
366 status = Status.STATUS_COMMITTED;
367 }
368 else if (isOneResource())
369 {
370 // One phase commit
371 if (trace)
372 {
373 log.trace("One phase commit: One resource.");
374 }
375 commitResources(true);
376 } else
377 {
378 // Two phase commit
379 if (trace)
380 {
381 log.trace("Two phase commit: Many resources.");
382 }
383
384 if (!prepareResources())
385 {
386 boolean commitDecision =
387 status == Status.STATUS_PREPARED &&
388 (heuristicCode == HEUR_NONE ||
389 heuristicCode == XAException.XA_HEURCOM);
390
391 // TODO: Save decision to stable storage for recovery
392 // after system crash.
393
394 if (commitDecision)
395 commitResources(false);
396 } else
397 status = Status.STATUS_COMMITTED; // all was read-only
398 }
399 }
400
401 if (status != Status.STATUS_COMMITTED)
402 {
403 rollbackResources();
404 doAfterCompletion();
405 cancelTimeout();
406
407 // save off the cause throwable as Instance done resets it to null
408 Throwable causedByThrowable = cause;
409
410 instanceDone();
411
412 // throw jboss rollback exception with the saved off cause
413 throw new JBossRollbackException("Unable to commit, tx=" +
414 toString() + " status=" + getStringStatus(status),
415 causedByThrowable);
416 }
417
418 cancelTimeout();
419 doAfterCompletion();
420 instanceDone();
421 checkHeuristics();
422
423 if (trace)
424 {
425 log.trace("Committed OK, tx=" + this);
426 }
427
428 } finally {
429 transactionLocalMap.clear();
430 threads.clear();
431 unlock();
432 }
433 }
434
435 public void rollback()
436 throws java.lang.IllegalStateException,
437 java.lang.SecurityException,
438 SystemException
439 {
440 try
441 {
442 lock();
443
444 if (trace)
445 {
446 log.trace("rollback(): Entered, tx=" + toString() +
447 " status=" + getStringStatus(status));
448 }
449
450 switch (status)
451 {
452 case Status.STATUS_ACTIVE:
453 status = Status.STATUS_MARKED_ROLLBACK;
454 // fall through..
455 case Status.STATUS_MARKED_ROLLBACK:
456 doBeforeCompletion();
457 endResources();
458 rollbackResources();
459 cancelTimeout();
460 doAfterCompletion();
461 instanceDone();
462 // Cannot throw heuristic exception, so we just have to
463 // clear the heuristics without reporting.
464 heuristicCode = HEUR_NONE;
465 return;
466 case Status.STATUS_PREPARING:
467 // Set status to avoid race with prepareResources().
468 status = Status.STATUS_MARKED_ROLLBACK;
469 return; // commit() will do rollback.
470 default:
471 throw new IllegalStateException("Cannot rollback(), " +
472 "tx=" + toString() +
473 " status=" +
474 getStringStatus(status));
475 }
476 } finally {
477 transactionLocalMap.clear();
478 threads.clear();
479 Thread.interrupted();// clear timeout that did an interrupt
480 unlock();
481 }
482 }
483
484 public boolean delistResource(XAResource xaRes, int flag)
485 throws java.lang.IllegalStateException,
486 SystemException
487 {
488 if (xaRes == null)
489 throw new IllegalArgumentException("null xaRes");
490 if (flag != XAResource.TMSUCCESS &&
491 flag != XAResource.TMSUSPEND &&
492 flag != XAResource.TMFAIL)
493 throw new IllegalArgumentException("Bad flag: " + flag);
494
495 try
496 {
497 lock();
498
499 if (trace)
500 {
501 log.trace("delistResource(): Entered, tx=" +
502 toString() + " status=" + getStringStatus(status));
503 }
504
505 int idx = findResource(xaRes);
506
507 if (idx == -1)
508 throw new IllegalArgumentException("xaRes not enlisted");
509
510 switch (status)
511 {
512 case Status.STATUS_ACTIVE:
513 case Status.STATUS_MARKED_ROLLBACK:
514 break;
515 case Status.STATUS_PREPARING:
516 throw new IllegalStateException("Already started preparing.");
517 case Status.STATUS_ROLLING_BACK:
518 throw new IllegalStateException("Already started rolling back.");
519 case Status.STATUS_PREPARED:
520 throw new IllegalStateException("Already prepared.");
521 case Status.STATUS_COMMITTING:
522 throw new IllegalStateException("Already started committing.");
523 case Status.STATUS_COMMITTED:
524 throw new IllegalStateException("Already committed.");
525 case Status.STATUS_ROLLEDBACK:
526 throw new IllegalStateException("Already rolled back.");
527 case Status.STATUS_NO_TRANSACTION:
528 throw new IllegalStateException("No transaction.");
529 case Status.STATUS_UNKNOWN:
530 throw new IllegalStateException("Unknown state");
531 default:
532 throw new IllegalStateException("Illegal status: " + status);
533 }
534
535 try
536 {
537 if (resourceState[idx] == RS_ENDED && !resources[idx].isSameRM(xaRes)) {
538 // This RM always returns false on isSameRM. Further,
539 // the last resource has already been delisted.
540 log.warn("Resource already delisted. tx=" + toString());
541 return false;
542 }
543 endResource(idx, flag);
544 return true;
545 } catch (XAException xae)
546 {
547 logXAException(xae);
548 status = Status.STATUS_MARKED_ROLLBACK;
549 cause = xae;
550 return false;
551 }
552 } finally
553 {
554 unlock();
555 }
556 }
557
558 public boolean enlistResource(XAResource xaRes)
559 throws RollbackException,
560 java.lang.IllegalStateException,
561 SystemException
562 {
563 if (xaRes == null)
564 throw new IllegalArgumentException("null xaRes");
565
566 try
567 {
568 lock();
569
570 if (trace)
571 {
572 log.trace("enlistResource(): Entered, tx=" +
573 toString() + " status=" + getStringStatus(status));
574 }
575
576 switch (status)
577 {
578 case Status.STATUS_ACTIVE:
579 case Status.STATUS_PREPARING:
580 break;
581 case Status.STATUS_PREPARED:
582 throw new IllegalStateException("Already prepared.");
583 case Status.STATUS_COMMITTING:
584 throw new IllegalStateException("Already started committing.");
585 case Status.STATUS_COMMITTED:
586 throw new IllegalStateException("Already committed.");
587 case Status.STATUS_MARKED_ROLLBACK:
588 throw new RollbackException("Already marked for rollback");
589 case Status.STATUS_ROLLING_BACK:
590 throw new RollbackException("Already started rolling back.");
591 case Status.STATUS_ROLLEDBACK:
592 throw new RollbackException("Already rolled back.");
593 case Status.STATUS_NO_TRANSACTION:
594 throw new IllegalStateException("No transaction.");
595 case Status.STATUS_UNKNOWN:
596 throw new IllegalStateException("Unknown state");
597 default:
598 throw new IllegalStateException("Illegal status: " + status);
599 }
600
601 if (resourcesEnded)
602 throw new IllegalStateException("Too late to enlist resources");
603
604 // Add resource
605 try
606 {
607 int idx = findResource(xaRes);
608
609 if (idx != -1)
610 {
611 if (resourceState[idx] == RS_ENLISTED)
612 return false; // already enlisted
613 if (resourceState[idx] == RS_ENDED && !resources[idx].isSameRM(xaRes)) {
614 // this is a resource that returns false on all calls to
615 // isSameRM. Further, the last resource enlisted has
616 // already been delisted, so it is time to enlist it again.
617 idx = -1;
618 } else {
619 startResource(idx);
620 return true;
621 }
622 }
623
624 for (int i = 0; i < resourceCount; ++i) {
625 if (resourceSameRM[i] == -1 && xaRes.isSameRM(resources[i])) {
626 // The xaRes is new. We register the xaRes with the Xid
627 // that the RM has previously seen from this transaction,
628 // and note that it has the same RM.
629 startResource(addResource(xaRes, resourceXids[i], i));
630
631 return true;
632 }
633 }
634
635 // New resource and new RM: Create a new transaction branch.
636 startResource(addResource(xaRes, createXidBranch(), -1));
637 return true;
638 } catch (XAException xae)
639 {
640 logXAException(xae);
641 cause = xae;
642 return false;
643 }
644 } finally
645 {
646 unlock();
647 }
648
649 }
650
651 public int getStatus()
652 throws SystemException
653 {
654 if (done)
655 return Status.STATUS_NO_TRANSACTION;
656 return status;
657 }
658
659 public void registerSynchronization(Synchronization s)
660 throws RollbackException,
661 java.lang.IllegalStateException,
662 SystemException
663 {
664 if (s == null)
665 throw new IllegalArgumentException("Null synchronization");
666
667 try
668 {
669 lock();
670
671 if (trace)
672 {
673 log.trace("registerSynchronization(): Entered, " +
674 "tx=" + toString() +
675 " status=" + getStringStatus(status));
676 }
677
678 switch (status) {
679 case Status.STATUS_ACTIVE:
680 case Status.STATUS_PREPARING:
681 break;
682 case Status.STATUS_PREPARED:
683 throw new IllegalStateException("Already prepared.");
684 case Status.STATUS_COMMITTING:
685 throw new IllegalStateException("Already started committing.");
686 case Status.STATUS_COMMITTED:
687 throw new IllegalStateException("Already committed.");
688 case Status.STATUS_MARKED_ROLLBACK:
689 throw new RollbackException("Already marked for rollback");
690 case Status.STATUS_ROLLING_BACK:
691 throw new RollbackException("Already started rolling back.");
692 case Status.STATUS_ROLLEDBACK:
693 throw new RollbackException("Already rolled back.");
694 case Status.STATUS_NO_TRANSACTION:
695 throw new IllegalStateException("No transaction.");
696 case Status.STATUS_UNKNOWN:
697 throw new IllegalStateException("Unknown state");
698 default:
699 throw new IllegalStateException("Illegal status: " + status);
700 }
701
702 if (syncCount == syncAllocSize)
703 {
704 // expand table
705 syncAllocSize = 2 * syncAllocSize;
706
707 Synchronization[] sy = new Synchronization[syncAllocSize];
708 System.arraycopy(sync, 0, sy, 0, syncCount);
709 sync = sy;
710 }
711 sync[syncCount++] = s;
712 } finally
713 {
714 unlock();
715 }
716 }
717
718 public void setRollbackOnly()
719 throws java.lang.IllegalStateException,
720 SystemException
721 {
722 try {
723 lock();
724
725 if (trace)
726 log.trace("setRollbackOnly(): Entered, tx=" +
727 toString() + " status=" + getStringStatus(status));
728
729 switch (status) {
730 case Status.STATUS_ACTIVE:
731 case Status.STATUS_PREPARING:
732 case Status.STATUS_PREPARED:
733 status = Status.STATUS_MARKED_ROLLBACK;
734 // fall through..
735 case Status.STATUS_MARKED_ROLLBACK:
736 case Status.STATUS_ROLLING_BACK:
737 return;
738 case Status.STATUS_COMMITTING:
739 throw new IllegalStateException("Already started committing.");
740 case Status.STATUS_COMMITTED:
741 throw new IllegalStateException("Already committed.");
742 case Status.STATUS_ROLLEDBACK:
743 throw new IllegalStateException("Already rolled back.");
744 case Status.STATUS_NO_TRANSACTION:
745 throw new IllegalStateException("No transaction.");
746 case Status.STATUS_UNKNOWN:
747 throw new IllegalStateException("Unknown state");
748 default:
749 throw new IllegalStateException("Illegal status: " + status);
750 }
751 } finally {
752 unlock();
753 }
754 }
755
756 // Public --------------------------------------------------------
757
758 public void associateCurrentThread()
759 {
760 threads.add(Thread.currentThread());
761 }
762
763 public void disassociateCurrentThread()
764 {
765 threads.remove(Thread.currentThread());
766 Thread.interrupted();
767 }
768 public void clearThreads()
769 {
770 for (int i = 0; i < threads.size(); i++)
771 {
772 Thread t = (Thread)threads.get(i);
773 }
774 }
775 public int hashCode()
776 {
777 return globalId.hashCode();
778 }
779
780 public String toString()
781 {
782 return "TransactionImpl:" + xidFactory.toString(xid);
783 }
784
785 public boolean equals(Object obj)
786 {
787 if (obj != null && obj instanceof TransactionImpl)
788 return globalId.equals(((TransactionImpl)obj).globalId);
789 return false;
790 }
791
792
793 // Package protected ---------------------------------------------
794
795 /**
796 * Getter for property done.
797 */
798 boolean isDone()
799 {
800 return done;
801 }
802
803 /**
804 * Return the global id of this transaction.
805 */
806 GlobalId getGlobalId()
807 {
808 return globalId;
809 }
810
811
812 // Private -------------------------------------------------------
813
814 /**
815 * Interrupt all threads involved with transaction
816 * This is called on timeout
817 */
818 private void interruptThreads()
819 {
820 Iterator it = threads.iterator();
821 while (it.hasNext())
822 {
823 Thread thread = (Thread)it.next();
824 try
825 {
826 thread.interrupt();
827 }
828 catch (Exception ignored) {}
829 }
830 threads.clear();
831 }
832
833 /**
834 * Return a string representation of the given status code.
835 */
836 private String getStringStatus(int status)
837 {
838 switch (status) {
839 case Status.STATUS_PREPARING:
840 return "STATUS_PREPARING";
841 case Status.STATUS_PREPARED:
842 return "STATUS_PREPARED";
843 case Status.STATUS_ROLLING_BACK:
844 return "STATUS_ROLLING_BACK";
845 case Status.STATUS_ROLLEDBACK:
846 return "STATUS_ROLLEDBACK";
847 case Status.STATUS_COMMITTING:
848 return "STATUS_COMMITING";
849 case Status.STATUS_COMMITTED:
850 return "STATUS_COMMITED";
851 case Status.STATUS_NO_TRANSACTION:
852 return "STATUS_NO_TRANSACTION";
853 case Status.STATUS_UNKNOWN:
854 return "STATUS_UNKNOWN";
855 case Status.STATUS_MARKED_ROLLBACK:
856 return "STATUS_MARKED_ROLLBACK";
857 case Status.STATUS_ACTIVE:
858 return "STATUS_ACTIVE";
859
860 default:
861 return "STATUS_UNKNOWN(" + status + ")";
862 }
863 }
864
865 /**
866 * Return a string representation of the given XA error code.
867 */
868 private String getStringXAErrorCode(int errorCode)
869 {
870 switch (errorCode) {
871 case XAException.XA_HEURCOM:
872 return "XA_HEURCOM";
873 case XAException.XA_HEURHAZ:
874 return "XA_HEURHAZ";
875 case XAException.XA_HEURMIX:
876 return "XA_HEURMIX";
877 case XAException.XA_HEURRB:
878 return "XA_HEURRB";
879
880 case XAException.XA_NOMIGRATE:
881 return "XA_NOMIGRATE";
882
883 case XAException.XA_RBCOMMFAIL:
884 return "XA_RBCOMMFAIL";
885 case XAException.XA_RBDEADLOCK:
886 return "XA_RBDEADLOCK";
887 case XAException.XA_RBINTEGRITY:
888 return "XA_RBINTEGRITY";
889 case XAException.XA_RBOTHER:
890 return "XA_RBOTHER";
891 case XAException.XA_RBPROTO:
892 return "XA_RBPROTO";
893 case XAException.XA_RBROLLBACK:
894 return "XA_RBROLLBACK";
895 case XAException.XA_RBTIMEOUT:
896 return "XA_RBTIMEOUT";
897 case XAException.XA_RBTRANSIENT:
898 return "XA_RBTRANSIENT";
899
900 case XAException.XA_RDONLY:
901 return "XA_RDONLY";
902 case XAException.XA_RETRY:
903 return "XA_RETRY";
904
905 case XAException.XAER_ASYNC:
906 return "XAER_ASYNC";
907 case XAException.XAER_DUPID:
908 return "XAER_DUPID";
909 case XAException.XAER_INVAL:
910 return "XAER_INVAL";
911 case XAException.XAER_NOTA:
912 return "XAER_NOTA";
913 case XAException.XAER_OUTSIDE:
914 return "XAER_OUTSIDE";
915 case XAException.XAER_PROTO:
916 return "XAER_PROTO";
917 case XAException.XAER_RMERR:
918 return "XAER_RMERR";
919 case XAException.XAER_RMFAIL:
920 return "XAER_RMFAIL";
921
922 default:
923 return "XA_UNKNOWN(" + errorCode + ")";
924 }
925 }
926
927 private void logXAException(XAException xae)
928 {
929 log.warn("XAException: tx=" + toString() + " errorCode=" +
930 getStringXAErrorCode(xae.errorCode), xae);
931 if (txManagerService != null)
932 {
933 txManagerService.formatXAException(xae, log);
934 } // end of if ()
935 }
936
937 /**
938 * Lock this instance.
939 */
940 private synchronized void lock()
941 {
942 if (done)
943 throw new IllegalStateException("Transaction has terminated");
944
945 if (locked) {
946 log.warn("Lock contention, tx=" + toString());
947 //DEBUG Thread.currentThread().dumpStack();
948
949 while (locked) {
950 try {
951 // Wakeup happens when:
952 // - notify() is called from unlock()
953 // - notifyAll is called from instanceDone()
954 wait();
955 } catch (InterruptedException ex) {
956 // ignore
957 }
958
959 if (done)
960 throw new IllegalStateException("Transaction has now terminated");
961 }
962 }
963
964 locked = true;
965 }
966
967 /**
968 * Unlock this instance.
969 */
970 private synchronized void unlock()
971 {
972 if (!locked)
973 {
974 log.warn("Unlocking, but not locked, tx=" + toString(),
975 new Throwable("[Stack trace]"));
976 }
977
978 locked = false;
979
980 notify();
981 }
982
983 /**
984 * Mark this transaction as non-existing.
985 */
986 private synchronized void instanceDone()
987 {
988 TxManager manager = TxManager.getInstance();
989
990 if (status == Status.STATUS_COMMITTED)
991 manager.incCommitCount();
992 else
993 manager.incRollbackCount();
994
995 // Garbage collection
996 manager.releaseTransactionImpl(this);
997
998 // Set the status
999 status = Status.STATUS_NO_TRANSACTION;
1000
1001 // Clear tables refering to external objects.
1002 // Even if a client holds on to this instance forever, the objects
1003 // that we have referenced may be garbage collected.
1004 sync = null;
1005 resources = null;
1006
1007 // Notify all threads waiting for the lock.
1008 notifyAll();
1009
1010 // set the done flag
1011 done = true;
1012 }
1013
1014 /**
1015 * Cancel the timeout.
1016 * This will release the lock while calling out.
1017 */
1018 private void cancelTimeout()
1019 {
1020 if (timeout != null) {
1021 unlock();
1022 try
1023 {
1024 timeout.cancel();
1025 } catch (Exception e)
1026 {
1027 if (trace)
1028 log.trace("failed to cancel timeout", e);
1029 } finally
1030 {
1031 lock();
1032 }
1033 timeout = null;
1034 }
1035 }
1036
1037 /**
1038 * Return index of XAResource, or <code>-1</code> if not found.
1039 */
1040 private int findResource(XAResource xaRes)
1041 {
1042 // A linear search may seem slow, but please note that
1043 // the number of XA resources registered with a transaction
1044 // are usually low.
1045 // Note: This searches backwards intentionally! It ensures that
1046 // if this resource was enlisted multiple times, then the last one
1047 // will be returned. All others should be in the state RS_ENDED.
1048 // This allows ResourceManagers that always return false from isSameRM
1049 // to be enlisted and delisted multiple times.
1050 for (int idx = resourceCount - 1; idx >= 0; --idx)
1051 if (xaRes == resources[idx])
1052 return idx;
1053
1054 return -1;
1055 }
1056
1057 /**
1058 * Add a resource, expanding tables if needed.
1059 *
1060 * @param xaRes The new XA resource to add. It is assumed that the
1061 * resource is not already in the table of XA resources.
1062 * @param branchXid The Xid for the transaction branch that is to
1063 * be used for associating with this resource.
1064 * @param idxSameRM The index in our XA resource tables of the first
1065 * XA resource having the same resource manager as
1066 * <code>xaRes</code>, or <code>-1</code> if <code>xaRes</code>
1067 * is the first resource seen with this resource manager.
1068 *
1069 * @return The index of the new resource in our internal tables.
1070 */
1071 private int addResource(XAResource xaRes, Xid branchXid, int idxSameRM)
1072 {
1073 if (resourceCount == resourceAllocSize)
1074 {
1075 // expand tables
1076 resourceAllocSize = 2 * resourceAllocSize;
1077
1078 XAResource[] res = new XAResource[resourceAllocSize];
1079 System.arraycopy(resources, 0, res, 0, resourceCount);
1080 resources = res;
1081
1082 int[] stat = new int[resourceAllocSize];
1083 System.arraycopy(resourceState, 0, stat, 0, resourceCount);
1084 resourceState = stat;
1085
1086 Xid[] xids = new Xid[resourceAllocSize];
1087 System.arraycopy(resourceXids, 0, xids, 0, resourceCount);
1088 resourceXids = xids;
1089
1090 int[] sameRM = new int[resourceAllocSize];
1091 System.arraycopy(resourceSameRM, 0, sameRM, 0, resourceCount);
1092 resourceSameRM = sameRM;
1093 }
1094 resources[resourceCount] = xaRes;
1095 resourceState[resourceCount] = RS_NEW;
1096 resourceXids[resourceCount] = branchXid;
1097 resourceSameRM[resourceCount] = idxSameRM;
1098
1099 return resourceCount++;
1100 }
1101
1102 /**
1103 * Call <code>start()</code> on a XAResource and update
1104 * internal state information.
1105 * This will release the lock while calling out.
1106 *
1107 * @param idx The index of the resource in our internal tables.
1108 */
1109 private void startResource(int idx)
1110 throws XAException
1111 {
1112 int flags = XAResource.TMJOIN;
1113
1114 if (resourceSameRM[idx] == -1)
1115 {
1116 switch (resourceState[idx])
1117 {
1118 case RS_NEW:
1119 flags = XAResource.TMNOFLAGS;
1120 break;
1121 case RS_SUSPENDED:
1122 flags = XAResource.TMRESUME;
1123 break;
1124
1125 default:
1126 if (trace)
1127 {
1128 log.trace("Unhandled resource state: " + resourceState[idx] +
1129 " (not RS_NEW or RS_SUSPENDED, using TMJOIN flags)");
1130 }
1131 }
1132 }
1133
1134 if (trace)
1135 {
1136 log.trace("startResource(" +
1137 xidFactory.toString(resourceXids[idx]) +
1138 ") entered: " + resources[idx].toString() +
1139 " flags=" + flags);
1140 }
1141
1142 unlock();
1143 // OSH FIXME: resourceState could be incorrect during this callout.
1144 try
1145 {
1146 try
1147 {
1148 resources[idx].start(resourceXids[idx], flags);
1149 }
1150 catch(XAException e)
1151 {
1152 throw e;
1153 }
1154 catch (Throwable t)
1155 {
1156 if (trace)
1157 {
1158 log.trace("unhandled throwable error in startResource", t);
1159 }
1160 status = Status.STATUS_MARKED_ROLLBACK;
1161 return;
1162 }
1163
1164 // Now the XA resource is associated with a transaction.
1165 resourceState[idx] = RS_ENLISTED;
1166 }
1167 finally
1168 {
1169 lock();
1170 if (trace)
1171 {
1172 log.trace("startResource(" +
1173 xidFactory.toString(resourceXids[idx]) +
1174 ") leaving: " + resources[idx].toString() +
1175 " flags=" + flags);
1176 }
1177 }
1178 }
1179
1180 /**
1181 * Call <code>end()</code> on the XAResource and update
1182 * internal state information.
1183 * This will release the lock while calling out.
1184 *
1185 * @param idx The index of the resource in our internal tables.
1186 * @param flag The flag argument for the end() call.
1187 */
1188 private void endResource(int idx, int flag)
1189 throws XAException
1190 {
1191 if (trace)
1192 {
1193 log.trace("endResource(" +
1194 xidFactory.toString(resourceXids[idx]) +
1195 ") entered: " + resources[idx].toString() +
1196 " flag=" + flag);
1197 }
1198
1199 unlock();
1200 // OSH FIXME: resourceState could be incorrect during this callout.
1201 try
1202 {
1203 try
1204 {
1205 resources[idx].end(resourceXids[idx], flag);
1206 } catch(XAException e)
1207 {
1208 throw e;
1209 } catch (Throwable t)
1210 {
1211 if (trace)
1212 {
1213 log.trace("unhandled throwable error in endResource", t);
1214 }
1215 status = Status.STATUS_MARKED_ROLLBACK;
1216 // Resource may or may not be ended after illegal exception.
1217 // We just assume it ended.
1218 resourceState[idx] = RS_ENDED;
1219 return;
1220 }
1221
1222
1223 // Update our internal state information
1224 if (flag == XAResource.TMSUSPEND)
1225 resourceState[idx] = RS_SUSPENDED;
1226 else
1227 {
1228 if (flag == XAResource.TMFAIL)
1229 {
1230
1231 status = Status.STATUS_MARKED_ROLLBACK;
1232 }
1233 resourceState[idx] = RS_ENDED;
1234 }
1235 } finally
1236 {
1237 lock();
1238 if (trace)
1239 {
1240 log.trace("endResource(" +
1241 xidFactory.toString(resourceXids[idx]) +
1242 ") leaving: " + resources[idx].toString() +
1243 " flag=" + flag);
1244 }
1245 }
1246 }
1247
1248 /**
1249 * End Tx association for all resources.
1250 */
1251 private void endResources()
1252 {
1253 for (int idx = 0; idx < resourceCount; idx++) {
1254 try {
1255 /*We don't have minerva crap any more! If your adapter doesn't
1256 like this, use the matchConnectionWithTx flag to prevent
1257 your resources from getting suspended.
1258 if (resourceState[idx] == RS_SUSPENDED) {
1259 // This is mad, but JTA 1.0.1 spec says on page 41:
1260 // "If TMSUSPEND is specified in flags, the transaction
1261 // branch is temporarily suspended in incomplete state.
1262 // The transaction context is in suspened state and must
1263 // be resumed via start with TMRESUME specified."
1264 // Note the _must_ above: It does not say _may_.
1265 // The above citation also seem to contradict the XA resource
1266 // state table on pages 17-18 where it is legal to do both
1267 // end(TMSUCCESS) and end(TMFAIL) when the resource is in
1268 // a suspended state.
1269 // But the Minerva XA pool does not like that we call end()
1270 // two times in a row, so we resume before ending.
1271 startResource(idx);
1272 }*/
1273 if (resourceState[idx] == RS_ENLISTED || resourceState[idx] == RS_SUSPENDED)
1274 {
1275 if (trace)
1276 log.trace("endresources(" + idx + "): state=" +
1277 resourceState[idx]);
1278 endResource(idx, XAResource.TMSUCCESS);
1279 }
1280 } catch(XAException xae)
1281 {
1282 logXAException(xae);
1283 status = Status.STATUS_MARKED_ROLLBACK;
1284 cause = xae;
1285 }
1286 }
1287 resourcesEnded = true; // Too late to enlist new resources.
1288 }
1289
1290
1291 /**
1292 * Call synchronization <code>beforeCompletion()</code>.
1293 * This will release the lock while calling out.
1294 */
1295 private void doBeforeCompletion()
1296 {
1297 unlock();
1298 try
1299 {
1300 for (int i = 0; i < syncCount; i++)
1301 {
1302 try
1303 {
1304 if (trace)
1305 {
1306 log.trace("calling sync " + i + ", " + sync[i]);
1307 } // end of if ()
1308 sync[i].beforeCompletion();
1309 } catch (Throwable t)
1310 {
1311 if (trace)
1312 {
1313 log.trace("failed before completion", t);
1314 }
1315 status = Status.STATUS_MARKED_ROLLBACK;
1316
1317 // save the cause off so the user can inspect it
1318 cause = t;
1319 break;
1320 }
1321 }
1322 } finally
1323 {
1324 lock();
1325 }
1326 }
1327
1328 /**
1329 * Call synchronization <code>afterCompletion()</code>.
1330 * This will release the lock while calling out.
1331 */
1332 private void doAfterCompletion()
1333 {
1334 // Assert: Status indicates: Too late to add new synchronizations.
1335 unlock();
1336 try
1337 {
1338 for (int i = 0; i < syncCount; i++)
1339 {
1340 try
1341 {
1342 sync[i].afterCompletion(status);
1343 } catch (Throwable t)
1344 {
1345 if (trace)
1346 {
1347 log.trace("failed after completion", t);
1348 }
1349 }
1350 }
1351 } finally
1352 {
1353 lock();
1354 }
1355 }
1356
1357 /**
1358 * We got another heuristic.
1359 *
1360 * Promote <code>heuristicCode</code> if needed and tell
1361 * the resource to forget the heuristic.
1362 * This will release the lock while calling out.
1363 *
1364 * @param resIdx The index of the XA resource that got a
1365 * heurictic in our internal tables, or <code>-1</code>
1366 * if the heuristic came from here.
1367 * @param code The heuristic code, one of
1368 * <code>XAException.XA_HEURxxx</code>.
1369 */
1370 private void gotHeuristic(int resIdx, int code)
1371 {
1372 switch (code)
1373 {
1374 case XAException.XA_HEURMIX:
1375 heuristicCode = XAException.XA_HEURMIX;
1376 break;
1377 case XAException.XA_HEURRB:
1378 if (heuristicCode == HEUR_NONE)
1379 heuristicCode = XAException.XA_HEURRB;
1380 else if (heuristicCode == XAException.XA_HEURCOM ||
1381 heuristicCode == XAException.XA_HEURHAZ)
1382 heuristicCode = XAException.XA_HEURMIX;
1383 break;
1384 case XAException.XA_HEURCOM:
1385 if (heuristicCode == HEUR_NONE)
1386 heuristicCode = XAException.XA_HEURCOM;
1387 else if (heuristicCode == XAException.XA_HEURRB ||
1388 heuristicCode == XAException.XA_HEURHAZ)
1389 heuristicCode = XAException.XA_HEURMIX;
1390 break;
1391 case XAException.XA_HEURHAZ:
1392 if (heuristicCode == HEUR_NONE)
1393 heuristicCode = XAException.XA_HEURHAZ;
1394 else if (heuristicCode == XAException.XA_HEURCOM ||
1395 heuristicCode == XAException.XA_HEURRB)
1396 heuristicCode = XAException.XA_HEURMIX;
1397 break;
1398 default:
1399 throw new IllegalArgumentException();
1400 }
1401
1402 if (resIdx != -1)
1403 {
1404 try
1405 {
1406 unlock();
1407 resources[resIdx].forget(resourceXids[resIdx]);
1408 } catch (XAException xae)
1409 {
1410 logXAException(xae);
1411 cause = xae;
1412 } finally
1413 {
1414 lock();
1415 }
1416 resourceState[resIdx] = RS_FORGOT;
1417 }
1418 }
1419
1420 /**
1421 * Check for heuristics, clear and throw exception if any found.
1422 */
1423 private void checkHeuristics()
1424 throws HeuristicMixedException, HeuristicRollbackException
1425 {
1426 switch (heuristicCode)
1427 {
1428 case XAException.XA_HEURHAZ:
1429 case XAException.XA_HEURMIX:
1430 heuristicCode = HEUR_NONE;
1431 if (trace)
1432 {
1433 log.trace("Throwing HeuristicMixedException, " +
1434 "status=" + getStringStatus(status));
1435 }
1436 throw new HeuristicMixedException();
1437 case XAException.XA_HEURRB:
1438 heuristicCode = HEUR_NONE;
1439 if (trace)
1440 {
1441 log.trace("Throwing HeuristicRollbackException, " +
1442 "status=" + getStringStatus(status));
1443 }
1444 throw new HeuristicRollbackException();
1445 case XAException.XA_HEURCOM:
1446 heuristicCode = HEUR_NONE;
1447 // Why isn't HeuristicCommitException used in JTA ?
1448 // And why define something that is not used ?
1449 // For now we just have to ignore this failure, even if it happened
1450 // on rollback.
1451 if (trace)
1452 {
1453 log.trace("NOT Throwing HeuristicCommitException, " +
1454 "status=" + getStringStatus(status));
1455 }
1456 return;
1457 }
1458 }
1459
1460
1461 /**
1462 * Prepare all enlisted resources.
1463 * If the first phase of the commit process results in a decision
1464 * to commit the <code>status</code> will be
1465 * <code>Status.STATUS_PREPARED</code> on return.
1466 * Otherwise the <code>status</code> will be
1467 * <code>Status.STATUS_MARKED_ROLLBACK</code> on return.
1468 * This will release the lock while calling out.
1469 *
1470 * @returns True iff all resources voted read-only.
1471 */
1472 private boolean prepareResources()
1473 {
1474 boolean readOnly = true;
1475
1476 status = Status.STATUS_PREPARING;
1477
1478 for (int i = 0; i < resourceCount; i++)
1479 {
1480 // Abort prepare on state change.
1481 if (status != Status.STATUS_PREPARING)
1482 return false;
1483
1484 if (resourceSameRM[i] != -1)
1485 continue; // This RM already prepared.
1486
1487 XAResource resource = resources[i];
1488
1489 try
1490 {
1491 int vote;
1492
1493 unlock();
1494 try
1495 {
1496 vote = resources[i].prepare(resourceXids[i]);
1497 } finally
1498 {
1499 lock();
1500 }
1501
1502 if (vote == XAResource.XA_OK)
1503 {
1504 readOnly = false;
1505 resourceState[i] = RS_VOTE_OK;
1506 } else if (vote == XAResource.XA_RDONLY)
1507 resourceState[i] = RS_VOTE_READONLY;
1508 else
1509 {
1510 // Illegal vote: rollback.
1511 if (trace)
1512 {
1513 log.trace("illegal vote in prepare resources", new Exception());
1514 } // end of if ()
1515 status = Status.STATUS_MARKED_ROLLBACK;
1516 return false;
1517 }
1518 } catch (XAException e)
1519 {
1520 readOnly = false;
1521
1522 logXAException(e);
1523
1524 switch (e.errorCode)
1525 {
1526 case XAException.XA_HEURCOM:
1527 // Heuristic commit is not that bad when preparing.
1528 // But it means trouble if we have to rollback.
1529 gotHeuristic(i, e.errorCode);
1530 break;
1531 case XAException.XA_HEURRB:
1532 case XAException.XA_HEURMIX:
1533 case XAException.XA_HEURHAZ:
1534 gotHeuristic(i, e.errorCode);
1535 if (status == Status.STATUS_PREPARING)
1536 status = Status.STATUS_MARKED_ROLLBACK;
1537 break;
1538 default:
1539 cause = e;
1540 if (status == Status.STATUS_PREPARING)
1541 status = Status.STATUS_MARKED_ROLLBACK;
1542 break;
1543 }
1544 } catch (Throwable t)
1545 {
1546 if (trace)
1547 {
1548 log.trace("unhandled throwable in prepareResources", t);
1549 }
1550 if (status == Status.STATUS_PREPARING)
1551 status = Status.STATUS_MARKED_ROLLBACK;
1552 cause = t;
1553 }
1554 }
1555
1556 if (status == Status.STATUS_PREPARING)
1557 status = Status.STATUS_PREPARED;
1558
1559 return readOnly;
1560 }
1561
1562 /**
1563 * Commit all enlisted resources.
1564 * This will release the lock while calling out.
1565 */
1566 private void commitResources(boolean onePhase)
1567 {
1568 status = Status.STATUS_COMMITTING;
1569
1570 for (int i = 0; i < resourceCount; i++)
1571 {
1572 if (trace)
1573 {
1574 log.trace("Committing resources, resourceStates["+i+"]=" +
1575 resourceState[i]);
1576 }
1577
1578 if (!onePhase && resourceState[i] != RS_VOTE_OK)
1579 continue; // Voted read-only at prepare phase.
1580
1581 if (resourceSameRM[i] != -1)
1582 continue; // This RM already committed.
1583
1584 // Abort commit on state change.
1585 if (status != Status.STATUS_COMMITTING)
1586 return;
1587
1588 try
1589 {
1590 unlock();
1591 try
1592 {
1593 resources[i].commit(resourceXids[i], onePhase);
1594 } finally
1595 {
1596 lock();
1597 }
1598 } catch (XAException e) {
1599 logXAException(e);
1600 switch (e.errorCode) {
1601 case XAException.XA_HEURRB:
1602 case XAException.XA_HEURCOM:
1603 case XAException.XA_HEURMIX:
1604 case XAException.XA_HEURHAZ:
1605 //usually throws an exception, but not for a couple of cases.
1606 gotHeuristic(i, e.errorCode);
1607 //May not be correct for HEURCOM
1608 //Two phase commit is committed after prepare is logged.
1609 if (onePhase)
1610 {
1611 status = Status.STATUS_MARKED_ROLLBACK;
1612 } // end of if ()
1613
1614 break;
1615 default:
1616 cause = e;
1617 if (onePhase)
1618 {
1619 status = Status.STATUS_MARKED_ROLLBACK;
1620 break;
1621 } // end of if ()
1622 //Not much we can do if there is an RMERR in the
1623 //commit phase of 2pc. I guess we try the other rms.
1624 }
1625 } catch (Throwable t)
1626 {
1627 if (trace)
1628 {
1629 log.trace("unhandled throwable in commitResources", t);
1630 }
1631 }
1632 }
1633
1634 if (status == Status.STATUS_COMMITTING)
1635 status = Status.STATUS_COMMITTED;
1636 }
1637
1638 /**
1639 * Rollback all enlisted resources.
1640 * This will release the lock while calling out.
1641 */
1642 private void rollbackResources()
1643 {
1644 status = Status.STATUS_ROLLING_BACK;
1645
1646 for (int i = 0; i < resourceCount; i++)
1647 {
1648 if (resourceState[i] == RS_VOTE_READONLY)
1649 {
1650 continue;
1651 }
1652 // Already forgotten
1653 if (resourceState[i] == RS_FORGOT)
1654 continue;
1655 if (resourceSameRM[i] != -1)
1656 {
1657 continue; // This RM already rolled back.
1658 }
1659 try
1660 {
1661 unlock();
1662 try
1663 {
1664 resources[i].rollback(resourceXids[i]);
1665 } finally
1666 {
1667 lock();
1668 }
1669 } catch (XAException e)
1670 {
1671 logXAException(e);
1672 switch (e.errorCode)
1673 {
1674 case XAException.XA_HEURRB:
1675 // Heuristic rollback is not that bad when rolling back.
1676 gotHeuristic(i, e.errorCode);
1677 continue;
1678 case XAException.XA_HEURCOM:
1679 case XAException.XA_HEURMIX:
1680 case XAException.XA_HEURHAZ:
1681 gotHeuristic(i, e.errorCode);
1682 continue;
1683 default:
1684 cause = e;
1685 break;
1686 }
1687 } catch (Throwable t) {
1688 if (trace)
1689 log.trace("unhandled throwable in rollbackResources", t);
1690 }
1691 }
1692
1693 status = Status.STATUS_ROLLEDBACK;
1694 }
1695
1696 /**
1697 * Create an Xid representing a new branch of this transaction.
1698 */
1699 private Xid createXidBranch()
1700 {
1701 long branchId = ++lastBranchId;
1702
1703 return xidFactory.newBranch(xid, branchId);
1704 }
1705
1706 /**
1707 * Check if we can do one-phase optimization.
1708 * We can do that only if no more than a single resource manager
1709 * is involved in this transaction.
1710 */
1711 private boolean isOneResource()
1712 {
1713 if (resourceCount == 1)
1714 return true;
1715
1716 // first XAResource surely has -1, it's the first!
1717 for (int i = 1; i < resourceCount; i++) {
1718 if (resourceSameRM[i] == -1) {
1719 // this one is not the same rm as previous ones,
1720 // there must be at least 2
1721 return false;
1722 }
1723
1724 }
1725 // all rms are the same one, one phase commit is ok.
1726 return true;
1727 }
1728
1729
1730 public long getTimeLeftBeforeTimeout()
1731 {
1732 return (start + timeoutPeriod) - System.currentTimeMillis();
1733 }
1734
1735 Object getTransactionLocalValue(TransactionLocal tlocal)
1736 {
1737 return transactionLocalMap.get(tlocal);
1738 }
1739
1740 void putTransactionLocalValue(TransactionLocal tlocal, Object value)
1741 {
1742 transactionLocalMap.put(tlocal, value);
1743 }
1744
1745 boolean containsTransactionLocal(TransactionLocal tlocal)
1746 {
1747 return transactionLocalMap.containsKey(tlocal);
1748 }
1749
1750 // Inner classes -------------------------------------------------
1751 }