001: /**********************************************************************
002: Copyright (c) 2007 Erik Bengtson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015: Contributors:
016: ...
017: **********************************************************************/package org.jpox.transaction;
018:
019: import java.util.ArrayList;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024: import java.util.Random;
025:
026: import javax.transaction.Synchronization;
027: import javax.transaction.xa.XAException;
028: import javax.transaction.xa.XAResource;
029: import javax.transaction.xa.Xid;
030:
031: import org.jpox.util.JPOXLogger;
032: import org.jpox.util.Localiser;
033: import org.omg.CORBA.SystemException;
034:
035: /**
036: * Transaction
037: * The Global Transaction ID is formed as: TX-<final static random int>-<static seq>-<current time millis>
038: */
039: public class Transaction {
040: /** this id of this instance **/
041: private static final int nodeId = new Random().nextInt();
042: /** sequence number for global transactions **/
043: private static int nextGlobalTransactionId = 1;
044: /** number for next branch **/
045: private int nextBranchId = 1;
046: /** transaction id **/
047: private final Xid xid;
048: /** transaction status **/
049: private int status;
050: /** has completing started ? **/
051: private boolean completing = false;
052: /** Synchonization **/
053: private List synchronization = new ArrayList();
054: /** enlisted XAResource resources **/
055: private List enlistedResources = new ArrayList();
056: /** branches - each resource is a new branch **/
057: private Map branches = new HashMap();
058: /** active branches are resources that have not ended and are not suspended **/
059: private Map activeBranches = new HashMap();
060: /** suspended branches **/
061: private Map suspendedResources = new HashMap();
062: /** Localisation utility for output messages */
063: protected static final Localiser LOCALISER = Localiser
064: .getInstance("org.jpox.Localisation");
065:
066: /**
067: *
068: */
069: Transaction() {
070: xid = new XidImpl(nodeId, 0, nextGlobalTransactionId++);
071: }
072:
073: public void commit() throws RollbackException,
074: HeuristicMixedException, HeuristicRollbackException,
075: SecurityException, IllegalStateException, SystemException {
076: if (completing) {
077: return;
078: }
079: if (status == Status.STATUS_MARKED_ROLLBACK) {
080: rollback();
081: return;
082: }
083:
084: try {
085: completing = true;
086:
087: // The transaction status must be ACTIVE
088: if (status != Status.STATUS_ACTIVE) {
089: throw new IllegalStateException();
090: }
091:
092: // Synchronization.beforeCompletion
093: Iterator syncIterator = synchronization.iterator();
094: while (syncIterator.hasNext()) {
095: Synchronization sync = (Synchronization) syncIterator
096: .next();
097: sync.beforeCompletion();
098: }
099:
100: List failures = null;
101: boolean failed = false;
102:
103: Iterator branchKeys = branches.keySet().iterator();
104:
105: if (enlistedResources.size() == 1) {
106:
107: // If we have only one resource, we dont ask to prepare, and we go with one phase commit
108: status = Status.STATUS_COMMITTING;
109: while (branchKeys.hasNext()) {
110: Object key = branchKeys.next();
111: XAResource resourceManager = (XAResource) branches
112: .get(key);
113: try {
114: if (!failed) {
115: resourceManager.commit(xid, true);
116: } else {
117: resourceManager.rollback(xid);
118: }
119: } catch (Throwable e) {
120: if (failures == null) {
121: //lazy instantiate this, because we only need on failures
122: failures = new ArrayList();
123: }
124: failures.add(e);
125: failed = true;
126: status = Status.STATUS_MARKED_ROLLBACK;
127: JPOXLogger.TRANSACTION.error(LOCALISER.msg(
128: "015038", "commit", resourceManager,
129: getXAErrorCode(e), toString()));
130: }
131: }
132: if (!failed) {
133: status = Status.STATUS_COMMITTED;
134: } else {
135: status = Status.STATUS_ROLLEDBACK;
136: }
137:
138: } else if (enlistedResources.size() > 0) {
139:
140: // Prepare each enlisted resource
141: status = Status.STATUS_PREPARING;
142: while ((!failed) && (branchKeys.hasNext())) {
143: Object key = branchKeys.next();
144: XAResource resourceManager = (XAResource) branches
145: .get(key);
146: try {
147: // Preparing the resource manager using its branch xid
148: resourceManager.prepare((Xid) key);
149: } catch (Throwable e) {
150: if (failures == null) {
151: //lazy instantiate this, because we only need on failures
152: failures = new ArrayList();
153: }
154: failures.add(e);
155: failed = true;
156: status = Status.STATUS_MARKED_ROLLBACK;
157: JPOXLogger.TRANSACTION.error(LOCALISER.msg(
158: "015038", "prepare", resourceManager,
159: getXAErrorCode(e), toString()));
160: }
161: }
162:
163: if (!failed) {
164: status = Status.STATUS_PREPARED;
165: }
166:
167: // Starts 2nd commit phase
168: // If fail, rollback
169: if (failed) {
170: status = Status.STATUS_ROLLING_BACK;
171: failed = false;
172: // Rolling back all the prepared (and unprepared) branches
173: branchKeys = branches.keySet().iterator();
174: while (branchKeys.hasNext()) {
175: Object key = branchKeys.next();
176: XAResource resourceManager = (XAResource) branches
177: .get(key);
178: try {
179: resourceManager.rollback((Xid) key);
180: } catch (Throwable e) {
181: JPOXLogger.TRANSACTION.error(LOCALISER.msg(
182: "015038", "rollback",
183: resourceManager, getXAErrorCode(e),
184: toString()));
185: if (failures == null) {
186: //lazy instantiate this, because we only need on failures
187: failures = new ArrayList();
188: }
189: failures.add(e);
190: failed = true;
191: }
192: }
193: status = Status.STATUS_ROLLEDBACK;
194: } else {
195: status = Status.STATUS_COMMITTING;
196: // Commit each enlisted resource
197: branchKeys = branches.keySet().iterator();
198: while (branchKeys.hasNext()) {
199: Object key = branchKeys.next();
200: XAResource resourceManager = (XAResource) branches
201: .get(key);
202: try {
203: resourceManager.commit((Xid) key, false);
204: } catch (Throwable e) {
205: JPOXLogger.TRANSACTION.error(LOCALISER.msg(
206: "015038", "commit",
207: resourceManager, getXAErrorCode(e),
208: toString()));
209: if (failures == null) {
210: //lazy instantiate this, because we only need on failures
211: failures = new ArrayList();
212: }
213: failures.add(e);
214: failed = true;
215: }
216: }
217: status = Status.STATUS_COMMITTED;
218: }
219: }
220:
221: // Synchronization.afterCompletion
222: syncIterator = synchronization.iterator();
223: while (syncIterator.hasNext()) {
224: Synchronization sync = (Synchronization) syncIterator
225: .next();
226: sync.afterCompletion(status);
227: }
228:
229: if (status == Status.STATUS_ROLLEDBACK) {
230: if (failed) {
231: if (failures.size() == 1) {
232: throw new HeuristicRollbackException(
233: "Transaction rolled back due to failure during commit",
234: (Exception) failures.get(0));
235: } else {
236: throw new HeuristicRollbackException(
237: "Multiple failures");
238: }
239: } else {
240: throw new RollbackException();
241: }
242: }
243: if ((status == Status.STATUS_COMMITTED) && (failed)) {
244: throw new HeuristicMixedException();
245: }
246:
247: } finally {
248: completing = false;
249: }
250: }
251:
252: public boolean delistResource(XAResource xaRes, int flag)
253: throws IllegalStateException, SystemException {
254: if (xaRes == null) {
255: return false;
256: }
257:
258: // The transaction status must be ACTIVE
259: if (status != Status.STATUS_ACTIVE) {
260: throw new IllegalStateException();
261: }
262:
263: Xid xid = (Xid) activeBranches.get(xaRes);
264:
265: if (xid == null) {
266: throw new IllegalStateException();
267: }
268:
269: activeBranches.remove(xaRes);
270:
271: if (JPOXLogger.TRANSACTION.isDebugEnabled()) {
272: JPOXLogger.TRANSACTION.debug(LOCALISER.msg("015039",
273: "delist", xaRes, getXAFlag(flag), toString()));
274: }
275:
276: XAException exception = null;
277:
278: try {
279: xaRes.end(xid, flag);
280: } catch (XAException e) {
281: exception = e;
282: }
283:
284: if (exception != null) {
285: JPOXLogger.TRANSACTION.error(LOCALISER.msg("015038",
286: "delist", xaRes, getXAErrorCode(exception),
287: toString()));
288: return false;
289: }
290:
291: if (flag == XAResource.TMSUSPEND) {
292: suspendedResources.put(xaRes, xid);
293: }
294: return true;
295:
296: }
297:
298: public boolean enlistResource(XAResource xaRes)
299: throws RollbackException, IllegalStateException,
300: SystemException {
301: if (xaRes == null) {
302: return false;
303: }
304:
305: if (status == Status.STATUS_MARKED_ROLLBACK) {
306: throw new RollbackException();
307: }
308:
309: // The transaction status must be ACTIVE
310: if (status != Status.STATUS_ACTIVE) {
311: throw new IllegalStateException();
312: }
313:
314: // Preventing two branches from being active at the same time on the
315: // same resource manager
316: Xid activeXid = (Xid) activeBranches.get(xaRes);
317: if (activeXid != null) {
318: return false;
319: }
320:
321: boolean alreadyEnlisted = false;
322: int flag = XAResource.TMNOFLAGS;
323:
324: Xid branchXid = (Xid) suspendedResources.get(xaRes);
325:
326: if (branchXid == null) {
327: Iterator enlistedIterator = enlistedResources.iterator();
328: while ((!alreadyEnlisted) && (enlistedIterator.hasNext())) {
329: XAResource resourceManager = (XAResource) enlistedIterator
330: .next();
331: try {
332: if (resourceManager.isSameRM(xaRes)) {
333: flag = XAResource.TMJOIN;
334: alreadyEnlisted = true;
335: }
336: } catch (XAException e) {
337: //do nothing
338: }
339: }
340: branchXid = new XidImpl(nextBranchId++, xid.getFormatId(),
341: xid.getGlobalTransactionId());
342: } else {
343: alreadyEnlisted = true;
344: flag = XAResource.TMRESUME;
345: suspendedResources.remove(xaRes);
346: }
347:
348: if (JPOXLogger.TRANSACTION.isDebugEnabled()) {
349: JPOXLogger.TRANSACTION.debug(LOCALISER.msg("015039",
350: "enlist", xaRes, getXAFlag(flag), toString()));
351: }
352:
353: try {
354: xaRes.start(branchXid, flag);
355: } catch (XAException e) {
356: JPOXLogger.TRANSACTION.error(LOCALISER.msg("015038",
357: "enlist", xaRes, getXAErrorCode(e), toString()));
358: return false;
359: }
360:
361: if (!alreadyEnlisted) {
362: enlistedResources.add(xaRes);
363: }
364:
365: branches.put(branchXid, xaRes);
366: activeBranches.put(xaRes, branchXid);
367:
368: return true;
369: }
370:
371: public int getStatus() throws SystemException {
372: return status;
373: }
374:
375: public void registerSynchronization(Synchronization arg0)
376: throws RollbackException, IllegalStateException,
377: SystemException {
378: if (arg0 == null) {
379: return;
380: }
381: if (status == Status.STATUS_MARKED_ROLLBACK) {
382: throw new RollbackException();
383: }
384: if (status != Status.STATUS_ACTIVE) {
385: throw new IllegalStateException();
386: }
387: synchronization.add(arg0);
388: }
389:
390: public void rollback() throws IllegalStateException,
391: SystemException {
392: if (completing) {
393: return;
394: }
395: try {
396: completing = true;
397:
398: // Must be ACTIVE and MARKED ROLLBACK
399: if (status != Status.STATUS_ACTIVE
400: && status != Status.STATUS_MARKED_ROLLBACK) {
401: throw new IllegalStateException();
402: }
403:
404: List failures = null;
405:
406: Iterator branchKeys = branches.keySet().iterator();
407:
408: status = Status.STATUS_ROLLING_BACK;
409: while (branchKeys.hasNext()) {
410: Xid xid = (Xid) branchKeys.next();
411: XAResource resourceManager = (XAResource) branches
412: .get(xid);
413: try {
414: resourceManager.rollback(xid);
415: } catch (Throwable e) {
416: if (failures == null) {
417: //lazy instantiate this, because we only need on failures
418: failures = new ArrayList();
419: }
420: failures.add(e);
421: JPOXLogger.TRANSACTION.error(LOCALISER.msg(
422: "015038", "rollback", resourceManager,
423: getXAErrorCode(e), toString()));
424: }
425: }
426: status = Status.STATUS_ROLLEDBACK;
427:
428: // Synchronization.afterCompletion
429: Iterator syncIterator = synchronization.iterator();
430: while (syncIterator.hasNext()) {
431: Synchronization sync = (Synchronization) syncIterator
432: .next();
433: sync.afterCompletion(status);
434: }
435: } finally {
436: completing = false;
437: }
438: }
439:
440: public void setRollbackOnly() throws IllegalStateException,
441: SystemException {
442: status = Status.STATUS_MARKED_ROLLBACK;
443: }
444:
445: public static String getXAErrorCode(Throwable xae) {
446: if (!(xae instanceof XAException)) {
447: return "UNKNOWN";
448: }
449: switch (((XAException) xae).errorCode) {
450: case XAException.XA_HEURCOM:
451: return "XA_HEURCOM";
452: case XAException.XA_HEURHAZ:
453: return "XA_HEURHAZ";
454: case XAException.XA_HEURMIX:
455: return "XA_HEURMIX";
456: case XAException.XA_HEURRB:
457: return "XA_HEURRB";
458: case XAException.XA_NOMIGRATE:
459: return "XA_NOMIGRATE";
460: case XAException.XA_RBBASE:
461: return "XA_RBBASE";
462: case XAException.XA_RBCOMMFAIL:
463: return "XA_RBCOMMFAIL";
464: case XAException.XA_RBDEADLOCK:
465: return "XA_RBBEADLOCK";
466: case XAException.XA_RBEND:
467: return "XA_RBEND";
468: case XAException.XA_RBINTEGRITY:
469: return "XA_RBINTEGRITY";
470: case XAException.XA_RBOTHER:
471: return "XA_RBOTHER";
472: case XAException.XA_RBPROTO:
473: return "XA_RBPROTO";
474: case XAException.XA_RBTIMEOUT:
475: return "XA_RBTIMEOUT";
476: case XAException.XA_RDONLY:
477: return "XA_RDONLY";
478: case XAException.XA_RETRY:
479: return "XA_RETRY";
480: case XAException.XAER_ASYNC:
481: return "XAER_ASYNC";
482: case XAException.XAER_DUPID:
483: return "XAER_DUPID";
484: case XAException.XAER_INVAL:
485: return "XAER_INVAL";
486: case XAException.XAER_NOTA:
487: return "XAER_NOTA";
488: case XAException.XAER_OUTSIDE:
489: return "XAER_OUTSIDE";
490: case XAException.XAER_PROTO:
491: return "XAER_PROTO";
492: case XAException.XAER_RMERR:
493: return "XAER_RMERR";
494: case XAException.XAER_RMFAIL:
495: return "XAER_RMFAIL";
496: default:
497: return "UNKNOWN";
498: }
499: }
500:
501: private static String getXAFlag(int flag) {
502: switch (flag) {
503: case XAResource.TMENDRSCAN:
504: return "TMENDRSCAN";
505: case XAResource.TMFAIL:
506: return "TMFAIL";
507: case XAResource.TMJOIN:
508: return "TMJOIN";
509: case XAResource.TMNOFLAGS:
510: return "TMNOFLAGS";
511: case XAResource.TMONEPHASE:
512: return "TMONEPHASE";
513: case XAResource.TMRESUME:
514: return "TMRESUME";
515: case XAResource.TMSTARTRSCAN:
516: return "TMSTARTRSCAN";
517: case XAResource.TMSUCCESS:
518: return "TMSUCCESS";
519: case XAResource.TMSUSPEND:
520: return "TMSUSPEND";
521: default:
522: return "UNKNOWN";
523: }
524: }
525:
526: public String toString() {
527: return "[JPOX Transaction, Transaction ID=" + xid.toString()
528: + ", enlisted resources="
529: + enlistedResources.toString() + "]";
530: }
531: }
|