0001: /*
0002: * @(#) ControlImpl.java 1.2 02/01/15
0003: *
0004: * JOTM: Java Open Transaction Manager
0005: *
0006: * This module was originally developed by
0007: *
0008: * - Bull S.A. as part of the JOnAS application server code released in
0009: * July 1999 (www.bull.com)
0010: *
0011: * --------------------------------------------------------------------------
0012: * The original code and portions created by Bull SA are
0013: * Copyright (c) 1999 BULL SA
0014: * All rights reserved.
0015: *
0016: * Redistribution and use in source and binary forms, with or without
0017: * modification, are permitted provided that the following conditions are met:
0018: *
0019: * -Redistributions of source code must retain the above copyright notice, this
0020: * list of conditions and the following disclaimer.
0021: *
0022: * -Redistributions in binary form must reproduce the above copyright notice,
0023: * this list of conditions and the following disclaimer in the documentation
0024: * and/or other materials provided with the distribution.
0025: *
0026: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0027: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0028: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0029: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0030: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0031: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0032: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0033: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0034: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0035: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0036: * POSSIBILITY OF SUCH DAMAGE.
0037: * --------------------------------------------------------------------------
0038: *
0039: * Contributor:
0040: * 02/01/15 Dean Jennings - ArrayList for resourceList and synchronizationList
0041: * 02/09/10 Riviere Guillaume (Guillaume.Riviere@inrialpes.fr) - RemoteControl added
0042: *
0043: * --------------------------------------------------------------------------
0044: * $Id: ControlImpl.java,v 1.30 2005/04/22 17:47:44 tonyortiz Exp $
0045: * --------------------------------------------------------------------------
0046: */
0047: package org.objectweb.jotm;
0048:
0049: import java.rmi.RemoteException;
0050: import java.rmi.NoSuchObjectException;
0051: import java.rmi.ServerException;
0052:
0053: import javax.rmi.PortableRemoteObject;
0054:
0055: import java.util.List;
0056: import java.util.ArrayList;
0057: import javax.transaction.Status;
0058: import javax.transaction.TransactionRolledbackException;
0059: import javax.transaction.xa.Xid;
0060:
0061: import org.objectweb.carol.util.configuration.ConfigurationRepository;
0062: import org.objectweb.carol.rmi.exception.RmiUtility;
0063:
0064: /**
0065: * Implementation of the object that represents a transaction.
0066: * This remote object has been created by a TransactionFactory.
0067: * It extends The RemoteControl Remote Interface
0068: *
0069: * @see org.objectweb.jotm.TransactionFactory
0070: *
0071: */
0072: public class ControlImpl extends PortableRemoteObject implements
0073: Control, Resource, Coordinator, Terminator,
0074: RecoveryCoordinator, TimerEventListener {
0075:
0076: // ===============================================================
0077: // Private data
0078: // ===============================================================
0079: /**
0080: * @serial
0081: */
0082: private List resourceList = new ArrayList();
0083: /**
0084: * @serial
0085: */
0086: private List synchronizationList = new ArrayList();
0087: /**
0088: * @serial
0089: */
0090: private int mystatus = Status.STATUS_UNKNOWN;
0091: /**
0092: * @serial
0093: */
0094: private boolean hasSupCoord = false;
0095: /**
0096: * @serial
0097: */
0098: private TimerEvent mytimer = null;
0099: /**
0100: * @serial
0101: */
0102: private Xid xid;
0103: /**
0104: * @serial
0105: */
0106: private Log mylog;
0107:
0108: // ---------------------------------------------------------------
0109: // Constructors
0110: // ---------------------------------------------------------------
0111:
0112: /**
0113: * Constructor for create or recreate
0114: *
0115: * @param timeout Timeout in seconds for this transaction
0116: * @param x Xid allocated to this transaction
0117: * @param supco Superior coordinator (null if create); must be a
0118: * org.objectweb.jotm.Coordinator or a org.omg.CosTransactions.Coordinator
0119: */
0120: ControlImpl(int timeout, Xid x, Object supco)
0121: throws RemoteException {
0122: if (TraceTm.jotm.isDebugEnabled()) {
0123: TraceTm.jotm.debug("timeout=" + timeout + ", xid=" + x
0124: + ", supco=" + supco);
0125: }
0126:
0127: // init object state
0128: mystatus = Status.STATUS_ACTIVE;
0129: xid = x;
0130: hasSupCoord = (supco != null);
0131:
0132: // XXX In case of sub-coordinator, should we arm a timer for rollback ???
0133:
0134: mytimer = TimerManager.getInstance().addTimer(this , timeout,
0135: new Integer(1), false);
0136:
0137: // If sub-coord, register resource
0138: if (supco != null) {
0139: try {
0140: if (supco instanceof Coordinator)
0141: ((Coordinator) supco).register_resource(this );
0142: else
0143: // XXX CORBA Cordinator not taken into account
0144: // register the control as a CORBA resource in the remote CORBA Coordinator (we use a wraper)
0145: //((org.omg.CosTransactions.Coordinator) supco).register_resource(new ControlResourceImpl(this));
0146: throw new RemoteException("Not Implemented");
0147: } catch (Exception e) {
0148: TraceTm.jotm.error(
0149: "Cannot register sub-coordinator:\n", e);
0150: }
0151: }
0152: }
0153:
0154: // ---------------------------------------------------------------
0155: // Control Interface
0156: // ---------------------------------------------------------------
0157:
0158: /**
0159: * Gets the Terminator object for this transaction
0160: *
0161: * @return Terminator for this transaction
0162: */
0163: public Terminator get_terminator() throws RemoteException {
0164:
0165: return this ;
0166: }
0167:
0168: /**
0169: * Gets the Coordinator object for this transaction
0170: *
0171: * @return Coordinator for this transaction
0172: */
0173: public Coordinator get_coordinator() throws RemoteException {
0174: if (TraceTm.jotm.isDebugEnabled()) {
0175: TraceTm.jotm.debug("ControlImpl.get_coordinator");
0176: }
0177: return this ;
0178: }
0179:
0180: // ---------------------------------------------------------------
0181: // Coordinator Interface
0182: // ---------------------------------------------------------------
0183:
0184: /**
0185: * Gets the current status of this transaction
0186: *
0187: * @return current transaction status
0188: */
0189: public int get_status() throws RemoteException {
0190: if (TraceTm.jotm.isDebugEnabled()) {
0191: TraceTm.jotm.debug("ControlImpl.get_status()");
0192: }
0193: return mystatus;
0194: }
0195:
0196: /**
0197: * Tests if the given coordinator represents this transaction
0198: *
0199: * @param tc Coordinator to be compared to this transaction
0200: * @return true if it is the same transaction
0201: */
0202: public boolean is_same_transaction(Coordinator tc)
0203: throws RemoteException {
0204: if (TraceTm.jotm.isDebugEnabled()) {
0205: TraceTm.jotm
0206: .debug("ControlImpl.is_same_transaction(Coordinator)");
0207: }
0208:
0209: String other = null;
0210:
0211: try {
0212: other = tc.get_transaction_name();
0213: } catch (Exception e) {
0214: if (TraceTm.jotm.isDebugEnabled()) {
0215: TraceTm.jotm
0216: .error(
0217: "ControlImpl.is_same_transaction() raised exception:\n",
0218: e);
0219: }
0220: return false;
0221: }
0222:
0223: return other.equals(get_transaction_name());
0224: }
0225:
0226: /**
0227: * Registers a Resource object for this transaction
0228: *
0229: * @param res Resource to be registered
0230: * @return RecoveryCoordinator used for replay_completion
0231: */
0232: public synchronized RecoveryCoordinator register_resource(
0233: Resource res) throws RemoteException {
0234: if (TraceTm.jotm.isDebugEnabled()) {
0235: TraceTm.jotm.debug("resource=" + res);
0236: TraceTm.jotm.debug("mystatus="
0237: + StatusHelper.getStatusName(mystatus));
0238: }
0239:
0240: switch (mystatus) {
0241:
0242: case Status.STATUS_ACTIVE:
0243: // Normal case
0244: resourceList.add(res);
0245: if (TraceTm.jotm.isDebugEnabled()) {
0246: TraceTm.jotm.debug("Resource registered");
0247: }
0248: break;
0249:
0250: case Status.STATUS_MARKED_ROLLBACK:
0251: case Status.STATUS_ROLLEDBACK:
0252: case Status.STATUS_ROLLING_BACK:
0253: TraceTm.jotm
0254: .error("ControlImpl.register_resource(): Transaction Rolled back");
0255: TraceTm.jotm.error("mystatus= "
0256: + StatusHelper.getStatusName(mystatus));
0257: throw new TransactionRolledbackException();
0258:
0259: default:
0260: TraceTm.jotm
0261: .error("ControlImpl.register_resource(): Transaction Inactive");
0262: TraceTm.jotm.error("mystatus= "
0263: + StatusHelper.getStatusName(mystatus));
0264: throw new InactiveException(
0265: "Cannot register resource, status "
0266: + StatusHelper.getStatusName(mystatus));
0267: }
0268: return this ;
0269: }
0270:
0271: /**
0272: * Registers a Synchronization object for this transaction
0273: *
0274: * @param sync RemoteSynchro to be registered
0275: */
0276: public synchronized void register_synchronization(RemoteSynchro sync)
0277: throws RemoteException {
0278: if (TraceTm.jotm.isDebugEnabled()) {
0279: TraceTm.jotm.debug("sync=" + sync);
0280: TraceTm.jotm.debug("mystatus="
0281: + StatusHelper.getStatusName(mystatus));
0282: }
0283:
0284: switch (mystatus) {
0285:
0286: case Status.STATUS_ACTIVE:
0287: // Normal case
0288: synchronizationList.add(sync);
0289: if (TraceTm.jotm.isDebugEnabled()) {
0290: TraceTm.jotm
0291: .debug("ControlImpl.register_synchronization(): RemoteSynchro registered");
0292: }
0293: break;
0294:
0295: case Status.STATUS_MARKED_ROLLBACK:
0296: case Status.STATUS_ROLLEDBACK:
0297: case Status.STATUS_ROLLING_BACK:
0298: TraceTm.jotm
0299: .error("ControlImpl.register_synchronization(): Transaction Rolled back");
0300: TraceTm.jotm.error("mystatus= "
0301: + StatusHelper.getStatusName(mystatus));
0302: throw new TransactionRolledbackException();
0303:
0304: default:
0305: TraceTm.jotm
0306: .error("ControlImpl.register_synchronization(): Transaction Inactive");
0307: TraceTm.jotm.error("mystatus= "
0308: + StatusHelper.getStatusName(mystatus));
0309: throw new InactiveException(
0310: "Cannot register synchronization, status "
0311: + StatusHelper.getStatusName(mystatus));
0312: }
0313: }
0314:
0315: /**
0316: * Asks to rollback the transaction
0317: *
0318: */
0319: public synchronized void rollback_only() throws RemoteException {
0320: if (TraceTm.jotm.isDebugEnabled()) {
0321: TraceTm.jotm.debug("mystatus="
0322: + StatusHelper.getStatusName(mystatus));
0323: }
0324:
0325: switch (mystatus) {
0326:
0327: case Status.STATUS_MARKED_ROLLBACK:
0328: // nothing to do: Already marked rolledback.
0329: if (TraceTm.jotm.isDebugEnabled()) {
0330: TraceTm.jotm
0331: .debug("ControlImpl.rollback_only(): Already marked rolledback");
0332: }
0333: break;
0334:
0335: case Status.STATUS_ACTIVE:
0336: // case Status.STATUS_COMMITTING :
0337: mystatus = Status.STATUS_MARKED_ROLLBACK;
0338: if (TraceTm.jotm.isDebugEnabled()) {
0339: TraceTm.jotm
0340: .debug("ControlImpl.rollback_only(): Marked rollback");
0341: }
0342: break;
0343:
0344: default:
0345: // already prepared
0346: TraceTm.jotm.error("ControlImpl.rollback_only(): Inactive");
0347: TraceTm.jotm.error("mystatus= "
0348: + StatusHelper.getStatusName(mystatus));
0349: throw new InactiveException(
0350: "Cannot rollback transaction, status "
0351: + StatusHelper.getStatusName(mystatus));
0352: }
0353: }
0354:
0355: /**
0356: * Gets a String that represents the transaction name.
0357: * Only the Format Id and the Global Id are used to build it :
0358: * The Branch Qualifier is not used.
0359: *
0360: * @return Transaction Name
0361: */
0362: public String get_transaction_name() throws RemoteException {
0363: if (TraceTm.jotm.isDebugEnabled()) {
0364: TraceTm.jotm.debug("ControlImpl.get_transaction_name()");
0365: }
0366:
0367: return xid.toString();
0368: }
0369:
0370: // ---------------------------------------------------------------
0371: // Terminator Interface
0372: // ---------------------------------------------------------------
0373:
0374: /**
0375: * Commits this transaction
0376: *
0377: * @param report_heuristics want to report heuristics if any
0378: *
0379: * @exception TransactionRolledbackException the transaction has been rolled back
0380: * @exception HeuristicMixed Resources have rolled back
0381: * @exception HeuristicHazard Resources may have rolled back
0382: */
0383: public synchronized void commit(boolean report_heuristics)
0384: throws RemoteException {
0385: if (TraceTm.jotm.isDebugEnabled()) {
0386: TraceTm.jotm
0387: .debug("report_heuristics=" + report_heuristics);
0388: TraceTm.jotm.debug("mystatus="
0389: + StatusHelper.getStatusName(mystatus));
0390: }
0391:
0392: String protocol = ConfigurationRepository
0393: .getCurrentConfiguration().getProtocol().getName();
0394:
0395: // Stops the Timer.
0396: if (mytimer != null) {
0397: mytimer.stop();
0398: }
0399:
0400: // Checks status
0401: switch (mystatus) {
0402:
0403: case Status.STATUS_ACTIVE:
0404: // normal case
0405: break;
0406:
0407: case Status.STATUS_COMMITTED:
0408: TraceTm.jotm
0409: .error("ControlImpl.commit(boolean): already done");
0410: return;
0411:
0412: case Status.STATUS_ROLLEDBACK:
0413: TraceTm.jotm
0414: .error("ControlImpl.commit(boolean): already rolled back");
0415: // We should be here in case of timeout expired.
0416: // In this case, completed() was not called.
0417: completed(true);
0418:
0419: if (protocol == null || !(protocol.equals("iiop"))) {
0420: throw new TransactionRolledbackException();
0421: }
0422:
0423: RmiUtility
0424: .rethrowRmiException(new TransactionRolledbackException());
0425:
0426: case Status.STATUS_MARKED_ROLLBACK:
0427: // Synchronization objects
0428: int errors = do_before_completion();
0429:
0430: if (errors > 0) {
0431: TraceTm.jotm
0432: .info("ControlImpl.commit(boolean): before completion error at rollback");
0433: }
0434:
0435: do_rollback(report_heuristics);
0436: completed(true);
0437:
0438: if (protocol == null || !(protocol.equals("iiop"))) {
0439: throw new TransactionRolledbackException();
0440: }
0441:
0442: RmiUtility
0443: .rethrowRmiException(new TransactionRolledbackException());
0444:
0445: default:
0446: TraceTm.jotm
0447: .error("ControlImpl.commit(boolean): bad status");
0448: TraceTm.jotm.error("mystatus= "
0449: + StatusHelper.getStatusName(mystatus));
0450:
0451: if (protocol == null || !(protocol.equals("iiop"))) {
0452: throw new HeuristicMixed();
0453: }
0454:
0455: RmiUtility.rethrowRmiException(new HeuristicMixed());
0456: }
0457:
0458: // Only one resource -> one phase commit.
0459: // If Resource raises an heuristic, forward it to the client.
0460: if (resourceList.size() == 1) {
0461: TraceTm.jotm.debug("1 resource");
0462:
0463: // Synchronization objects
0464: int errors = do_before_completion();
0465:
0466: if (errors > 0) {
0467: TraceTm.jotm
0468: .info("before completion error -> rollback");
0469: do_rollback(report_heuristics);
0470: completed(true);
0471:
0472: if (protocol == null || !(protocol.equals("iiop"))) {
0473: throw new TransactionRolledbackException();
0474: }
0475:
0476: RmiUtility
0477: .rethrowRmiException(new TransactionRolledbackException());
0478: }
0479:
0480: mystatus = Status.STATUS_COMMITTING;
0481:
0482: try {
0483: Resource res = (Resource) resourceList.get(0);
0484: res.commit_one_phase();
0485: mystatus = Status.STATUS_COMMITTED;
0486: } catch (TransactionRolledbackException e) {
0487: TraceTm.jotm
0488: .info("commit_one_phase = TransactionRolledbackException");
0489: mystatus = Status.STATUS_ROLLEDBACK;
0490: } catch (HeuristicHazard e) {
0491: TraceTm.jotm
0492: .info("commit_one_phase = HeuristicException");
0493: mystatus = Status.STATUS_UNKNOWN;
0494: } catch (NoSuchObjectException e) {
0495: // nothing done: can rollback transaction
0496: TraceTm.jotm
0497: .info("commit_one_phase = NoSuchObjectException");
0498: mystatus = Status.STATUS_ROLLEDBACK;
0499: } catch (ServerException e) { // with RMI, may encapsulate TransactionRolledbackException
0500: TraceTm.jotm
0501: .info("commit_one_phase = ServerException: "
0502: + e);
0503: mystatus = Status.STATUS_ROLLEDBACK;
0504: } catch (Exception e) {
0505: TraceTm.jotm.error(
0506: "commit_one_phase = Unexpected exception: ", e);
0507: mystatus = Status.STATUS_UNKNOWN;
0508: }
0509:
0510: // Synchronization objects
0511: do_after_completion();
0512:
0513: // Possibly report heuristics to the caller
0514: switch (mystatus) {
0515: case Status.STATUS_COMMITTED:
0516: completed(true);
0517: break;
0518: case Status.STATUS_ROLLEDBACK:
0519: completed(true);
0520:
0521: if (protocol == null || !(protocol.equals("iiop"))) {
0522: throw new TransactionRolledbackException();
0523: }
0524:
0525: RmiUtility
0526: .rethrowRmiException(new TransactionRolledbackException());
0527: case Status.STATUS_UNKNOWN:
0528: completed(false);
0529:
0530: if (report_heuristics) {
0531: if (protocol == null || !(protocol.equals("iiop"))) {
0532: throw new HeuristicHazard();
0533: }
0534:
0535: RmiUtility
0536: .rethrowRmiException(new HeuristicHazard());
0537: }
0538: }
0539: return;
0540: }
0541:
0542: // 2PC - phase 1
0543: int v = do_prepare(report_heuristics);
0544:
0545: if (TraceTm.jotm.isDebugEnabled()) {
0546: TraceTm.jotm.debug("Vote = " + v);
0547: }
0548:
0549: // Depending on vote, commits or rollbacks transaction
0550: switch (v) {
0551: case Resource.VOTE_COMMIT:
0552: if (TraceTm.jotm.isDebugEnabled()) {
0553: TraceTm.jotm
0554: .debug("ControlImpl.commit(boolean): committing Tx");
0555: }
0556: break;
0557: case Resource.VOTE_ROLLBACK:
0558: if (TraceTm.jotm.isDebugEnabled()) {
0559: TraceTm.jotm
0560: .debug("ControlImpl.commit(boolean): rolling back Tx");
0561: }
0562: do_rollback(report_heuristics);
0563: completed(true);
0564:
0565: if (protocol == null || !(protocol.equals("iiop"))) {
0566: throw new TransactionRolledbackException();
0567: }
0568:
0569: RmiUtility
0570: .rethrowRmiException(new TransactionRolledbackException());
0571: case Resource.VOTE_READONLY:
0572: // No commit phase.
0573: if (TraceTm.jotm.isDebugEnabled()) {
0574: TraceTm.jotm
0575: .debug("ControlImpl.commit(boolean): readonly resources");
0576: }
0577: mystatus = Status.STATUS_COMMITTED;
0578: completed(true);
0579: return;
0580: }
0581:
0582: // Prepare was OK: Decision to commit.
0583: mylog.flushLog(Log.DECISION_TO_COMMIT);
0584:
0585: // 2PC - phase 2
0586: if (do_commit(report_heuristics) == 0) {
0587: completed(true);
0588: } else {
0589: completed(false);
0590: }
0591: }
0592:
0593: // ---------------------------------------------------------------
0594: // Resource or Terminator Interface
0595: // rollback() has the same signature in Terminator and Resource!
0596: // ---------------------------------------------------------------
0597:
0598: /**
0599: * Rolls back this transaction branch. Can be a sub-coordinator or
0600: * a normal coordinator.
0601: *
0602: */
0603: public synchronized void rollback() throws RemoteException {
0604: if (TraceTm.jotm.isDebugEnabled()) {
0605: TraceTm.jotm.debug("mystatus="
0606: + StatusHelper.getStatusName(mystatus));
0607: }
0608:
0609: String protocol = ConfigurationRepository
0610: .getCurrentConfiguration().getProtocol().getName();
0611:
0612: // Stops the Timer.
0613: if (mytimer != null) {
0614: mytimer.stop();
0615: }
0616:
0617: // Checks status
0618: switch (mystatus) {
0619:
0620: case Status.STATUS_ACTIVE:
0621: case Status.STATUS_MARKED_ROLLBACK:
0622: // normal case
0623: break;
0624:
0625: case Status.STATUS_ROLLEDBACK:
0626: TraceTm.jotm
0627: .error("ControlImpl.rollback(): already rolled back");
0628: TraceTm.jotm.error("mystatus= "
0629: + StatusHelper.getStatusName(mystatus));
0630: return;
0631:
0632: default:
0633: TraceTm.jotm
0634: .error("ControlImpl.rollback(): rollback: bad status");
0635: TraceTm.jotm.error("mystatus= "
0636: + StatusHelper.getStatusName(mystatus));
0637: completed(false);
0638:
0639: if (protocol == null || !(protocol.equals("iiop"))) {
0640: throw new HeuristicMixed("rollback: bad status");
0641: }
0642:
0643: RmiUtility
0644: .rethrowRmiException(new TransactionRolledbackException());
0645: }
0646:
0647: // Superior coordinator.
0648: if (!hasSupCoord) {
0649: // Synchronization objects
0650: int errors = do_before_completion();
0651: if (errors > 0) {
0652: TraceTm.jotm
0653: .info("ControlImpl.rollback(): before completion error at rollback");
0654: }
0655: }
0656:
0657: // rollback Tx
0658: try {
0659: do_rollback(false);
0660: } catch (Exception e) {
0661: TraceTm.jotm
0662: .error(
0663: "ControlImpl.rollback(): rollback raised exception ",
0664: e);
0665: }
0666: completed(true);
0667: }
0668:
0669: // ---------------------------------------------------------------
0670: // Resource Interface (for sub-coordinators)
0671: // ---------------------------------------------------------------
0672:
0673: /**
0674: * Sub-coordinator has received prepare from its superior.
0675: * It must more or less do the same things that the phase 1 of the 2PC.
0676: *
0677: * @return Vote : commit, roollback or read-only.
0678: */
0679: public synchronized int prepare() throws RemoteException {
0680: if (TraceTm.jotm.isDebugEnabled()) {
0681: TraceTm.jotm.debug("mystatus="
0682: + StatusHelper.getStatusName(mystatus));
0683: }
0684:
0685: // Stops the Timer.
0686: if (mytimer != null) {
0687: mytimer.stop();
0688: }
0689:
0690: // Checks status
0691: switch (mystatus) {
0692:
0693: case Status.STATUS_ACTIVE:
0694: // normal case
0695: break;
0696:
0697: case Status.STATUS_COMMITTED:
0698: // Should not occur ?
0699: TraceTm.jotm
0700: .error("ControlImpl.prepare(): transaction already commited");
0701: TraceTm.jotm.error("mystatus= "
0702: + StatusHelper.getStatusName(mystatus));
0703: return Resource.VOTE_COMMIT;
0704:
0705: case Status.STATUS_ROLLEDBACK:
0706: // Should not occur ?
0707: TraceTm.jotm
0708: .error("ControlImpl.prepare(): transaction already rolled back");
0709: TraceTm.jotm.error("mystatus= "
0710: + StatusHelper.getStatusName(mystatus));
0711: return Resource.VOTE_ROLLBACK;
0712:
0713: case Status.STATUS_MARKED_ROLLBACK:
0714: do_rollback(false);
0715: completed(true);
0716: return Resource.VOTE_ROLLBACK;
0717:
0718: default:
0719: // Don't know what to do here...
0720: TraceTm.jotm.error("ControlImpl.prepare(): bad status");
0721: TraceTm.jotm.error("mystatus= "
0722: + StatusHelper.getStatusName(mystatus));
0723: completed(false);
0724: return Resource.VOTE_ROLLBACK;
0725: }
0726:
0727: int ret = do_prepare(false);
0728:
0729: switch (ret) {
0730: case Resource.VOTE_COMMIT:
0731: if (TraceTm.jotm.isDebugEnabled()) {
0732: TraceTm.jotm
0733: .debug("ControlImpl.prepare(): vote commit");
0734: }
0735: break;
0736: case Resource.VOTE_ROLLBACK:
0737: if (TraceTm.jotm.isDebugEnabled()) {
0738: TraceTm.jotm
0739: .debug("ControlImpl.prepare(): vote rollback");
0740: }
0741: do_rollback(false);
0742: completed(true);
0743: return ret;
0744: case Resource.VOTE_READONLY:
0745: if (TraceTm.jotm.isDebugEnabled()) {
0746: TraceTm.jotm
0747: .debug("ControlImpl.prepare(): vote readonly");
0748: }
0749: mystatus = Status.STATUS_COMMITTED;
0750: completed(true);
0751: return ret;
0752: }
0753:
0754: // Flush log + recovery coordinator
0755: // TODO
0756:
0757: return ret;
0758: }
0759:
0760: /**
0761: * Sub-coordinator received commit from its superior.
0762: * It must more or less do the same things that the phase 2 of the 2PC.
0763: */
0764: public synchronized void commit() throws RemoteException {
0765: if (TraceTm.jotm.isDebugEnabled()) {
0766: TraceTm.jotm.debug("mystatus="
0767: + StatusHelper.getStatusName(mystatus));
0768: }
0769:
0770: // check status
0771: switch (mystatus) {
0772: case Status.STATUS_PREPARED:
0773: // normal case
0774: break;
0775: default:
0776: // Don't know what to do here...
0777: TraceTm.jotm
0778: .error("ControlImpl.commit(): commit: bad status");
0779: TraceTm.jotm.error("mystatus= "
0780: + StatusHelper.getStatusName(mystatus));
0781: completed(false);
0782: return;
0783: }
0784:
0785: // send commit to resources
0786: if (do_commit(true) == 0) {
0787: completed(true);
0788: } else {
0789: completed(false);
0790: }
0791: }
0792:
0793: /**
0794: * Sub-coordinator received commit_one_phase from its superior.
0795: * It is more or less a Terminator.commit().
0796: */
0797: public void commit_one_phase() throws RemoteException {
0798: if (TraceTm.jotm.isDebugEnabled()) {
0799: TraceTm.jotm.debug("ControlImpl.commit_one_phase()");
0800: }
0801:
0802: // Just call Terminator.commit
0803: commit(true);
0804: }
0805:
0806: /**
0807: * forget transaction
0808: *
0809: */
0810: public synchronized void forget() throws RemoteException {
0811: if (TraceTm.jotm.isDebugEnabled()) {
0812: TraceTm.jotm.debug("ControlImpl.forget()");
0813: }
0814:
0815: completed(true);
0816: }
0817:
0818: // ---------------------------------------------------------------
0819: // RecoveryCoordinator Interface
0820: // ---------------------------------------------------------------
0821:
0822: /**
0823: * Asks the status of this transaction, after recovery of a Resource
0824: *
0825: * @param res Resource recovering
0826: */
0827: public int replay_completion(Resource res) throws RemoteException {
0828: if (TraceTm.jotm.isDebugEnabled()) {
0829: TraceTm.jotm.debug("res=" + res);
0830: }
0831: return mystatus;
0832: }
0833:
0834: // ===============================================================
0835: // TimerEventListener implementation
0836: // ===============================================================
0837:
0838: /**
0839: * The transaction timeout has expired
0840: * Do not synchronize this method to avoid deadlocks!
0841: */
0842: public void timeoutExpired(Object arg) {
0843: if (TraceTm.jotm.isDebugEnabled()) {
0844: TraceTm.jotm.debug("arg=" + arg);
0845: }
0846:
0847: int argvalue = ((Integer) arg).intValue();
0848:
0849: switch (argvalue) {
0850: case 1:
0851: if (TraceTm.jotm.isDebugEnabled()) {
0852: TraceTm.jotm
0853: .debug("ControlImpl.timeoutExpired(Object): timeout expired");
0854: }
0855:
0856: try {
0857: do_rollback(false);
0858: } catch (Exception e) {
0859: TraceTm.jotm
0860: .error(
0861: "ControlImpl.timeoutExpired(Object): rollback raised exception ",
0862: e);
0863: }
0864: break;
0865: case 2:
0866: if (TraceTm.jotm.isDebugEnabled()) {
0867: TraceTm.jotm
0868: .debug("ControlImpl.timeoutExpired(Object): removing ControlImpl");
0869: }
0870: explicit_destroy();
0871: break;
0872: default:
0873: TraceTm.jotm
0874: .error("ControlImpl.timeoutExpired(Object): timeoutExpired bad value="
0875: + argvalue);
0876: break;
0877: }
0878: }
0879:
0880: // ===============================================================
0881: // Private methods
0882: // ===============================================================
0883:
0884: /**
0885: * timeout expired on this transaction.
0886: * This method is not private, because it's used by Timers
0887: */
0888: void ding() {
0889: if (TraceTm.jotm.isDebugEnabled()) {
0890: TraceTm.jotm.debug("");
0891: }
0892: }
0893:
0894: /**
0895: * Phase 1 of the 2-Phase-Commit:
0896: * Sends prepare to each registered resource
0897: * This internal routine should be used either for prepare on sub-coordinator
0898: * or for commit on Terminator.
0899: *
0900: * @return global vote
0901: */
0902: private int do_prepare(boolean report_heuristics) {
0903: if (TraceTm.jotm.isDebugEnabled()) {
0904: TraceTm.jotm
0905: .debug("report_heuristics=" + report_heuristics);
0906: }
0907:
0908: int errors = 0;
0909: int ret = Resource.VOTE_READONLY;
0910:
0911: // Synchronization objects
0912: errors = do_before_completion();
0913:
0914: if (errors > 0) {
0915: if (TraceTm.jotm.isDebugEnabled()) {
0916: TraceTm.jotm
0917: .debug("before_completion failed -> rollback");
0918: }
0919: return Resource.VOTE_ROLLBACK;
0920: }
0921:
0922: // No resource -> just forget transaction.
0923: if (resourceList.size() == 0) {
0924: TraceTm.jotm.error("commit: no resource");
0925: mystatus = Status.STATUS_COMMITTED;
0926: do_after_completion();
0927: completed(true);
0928: return ret;
0929: }
0930:
0931: // Creates a log for that transaction, where we will add all the
0932: // resources that replied VOTE_COMMIT to prepare.
0933: // Do not flush the log on disk before decision to commit.
0934: mylog = new Log();
0935:
0936: // Sends prepare to each resource.
0937: // In case of prepare on sub-coord. we may have only 1 resource.
0938: // In case of phase 1 of the 2PC, we have several resources, because
0939: // the case of 1 resource is treated with commit_one_phase (optimization)
0940: mystatus = Status.STATUS_PREPARING;
0941:
0942: for (int i = 0; i < resourceList.size(); i++) {
0943: Resource res = (Resource) resourceList.get(i);
0944:
0945: if (errors > 0) {
0946: if (TraceTm.jotm.isWarnEnabled()) {
0947: TraceTm.jotm
0948: .warn("Vote stopped: at least one resource has voted rollback.");
0949: }
0950: break;
0951: } else {
0952: // No error yet: Send prepare to the resource.
0953: try {
0954: if (TraceTm.jotm.isDebugEnabled()) {
0955: TraceTm.jotm.debug("send prepare to resource");
0956: }
0957:
0958: switch (res.prepare()) {
0959: case Resource.VOTE_COMMIT:
0960: // Log resource
0961: mylog.addResource(res);
0962: TraceTm.jotm
0963: .info("Resource replied commit to prepare");
0964: ret = Resource.VOTE_COMMIT;
0965: break;
0966: case Resource.VOTE_ROLLBACK:
0967: TraceTm.jotm
0968: .info("Resource replied rollback to prepare");
0969: ret = Resource.VOTE_ROLLBACK;
0970: errors++;
0971: break;
0972: case Resource.VOTE_READONLY:
0973: if (TraceTm.jotm.isDebugEnabled()) {
0974: TraceTm.jotm
0975: .debug("Resource replied readonly to prepare");
0976: }
0977: break;
0978: }
0979: } catch (HeuristicHazard e) { // Subcoordinator only
0980: TraceTm.jotm.error("HeuristicHazard on prepare");
0981: ret = Resource.VOTE_ROLLBACK;
0982: errors++;
0983: } catch (HeuristicMixed e) { // Subcoordinator only
0984: TraceTm.jotm.error("HeuristicMixed on prepare");
0985: ret = Resource.VOTE_ROLLBACK;
0986: errors++;
0987: } catch (Exception e) {
0988: TraceTm.jotm.error("exception on prepare: ", e);
0989: ret = Resource.VOTE_ROLLBACK;
0990: errors++;
0991: }
0992: }
0993: }
0994:
0995: if (ret == Resource.VOTE_READONLY) {
0996: if (TraceTm.jotm.isDebugEnabled()) {
0997: TraceTm.jotm.debug("All resources returned Readonly");
0998: }
0999: mystatus = Status.STATUS_COMMITTED;
1000: // Synchronization objects
1001: do_after_completion();
1002: }
1003: if (ret == Resource.VOTE_COMMIT) {
1004: mystatus = Status.STATUS_PREPARED;
1005: }
1006:
1007: if (TraceTm.jotm.isDebugEnabled()) {
1008: if (TraceTm.jotm.isDebugEnabled()) {
1009: TraceTm.jotm.debug("Vote = " + ret);
1010: }
1011: }
1012: return ret;
1013: }
1014:
1015: /**
1016: * Phase 2 of the 2-Phase-Commit:
1017: * Sends commit to each registered resource
1018: * This internal routine should be used either for commit on sub-coordinator
1019: * or for commit on Terminator.
1020: *
1021: * @return 0 if commit OK.
1022: */
1023: private int do_commit(boolean report_heuristics)
1024: throws TransactionRolledbackException, HeuristicMixed,
1025: HeuristicHazard, HeuristicRollback {
1026:
1027: if (TraceTm.jotm.isDebugEnabled()) {
1028: TraceTm.jotm
1029: .debug("report_heuristics=" + report_heuristics);
1030: }
1031:
1032: // First check that a log is initialized
1033: if (mylog == null) {
1034: TraceTm.jotm.error("no log");
1035: return -1;
1036: }
1037:
1038: // Status Transaction = committing
1039: mystatus = Status.STATUS_COMMITTING;
1040:
1041: // Sends commit to each resource "prepared".
1042: int commitnb = 0;
1043: int heuristicnb = 0;
1044: int errors = 0;
1045: int heuristicstate = 0;
1046:
1047: for (int i = 0; i < mylog.resourceLogged.size(); i++) {
1048: ResourceInfo resinfo = (ResourceInfo) mylog.resourceLogged
1049: .elementAt(i);
1050:
1051: if (resinfo.mystate != ResourceInfo.PREPARED) {
1052: TraceTm.jotm.info("resource not prepared");
1053: continue;
1054: }
1055:
1056: // aborts transaction if error and no resource committed yet
1057: if (commitnb == 0 && errors > 0) {
1058: try {
1059: TraceTm.jotm.info("Send rollback to resource");
1060: resinfo.getResource().rollback();
1061: resinfo.mystate = ResourceInfo.ROLLEDBACK;
1062: } catch (HeuristicCommit e) {
1063: TraceTm.jotm.error("Heuristic commit");
1064: resinfo.mystate = ResourceInfo.HEURISTIC_COMMIT;
1065: heuristicstate = ResourceInfo.HEURISTIC_COMMIT;
1066: commitnb++;
1067: heuristicnb++;
1068: } catch (Exception e) {
1069: TraceTm.jotm.error("exception on rollback: ", e);
1070: }
1071: continue;
1072: }
1073:
1074: // commits resource
1075:
1076: try {
1077: TraceTm.jotm.debug("Send commit to resource");
1078: resinfo.getResource().commit();
1079: resinfo.mystate = ResourceInfo.COMMITTED;
1080: commitnb++;
1081: } catch (HeuristicRollback e) {
1082: TraceTm.jotm.error("Heuristic Rollback");
1083: resinfo.mystate = ResourceInfo.HEURISTIC_ROLLBACK;
1084: heuristicstate = ResourceInfo.HEURISTIC_ROLLBACK;
1085: errors++;
1086: if (commitnb > 0)
1087: heuristicnb++;
1088: } catch (HeuristicMixed e) {
1089: TraceTm.jotm.error("Heuristic Mixed");
1090: resinfo.mystate = ResourceInfo.HEURISTIC_MIXED;
1091: heuristicstate = ResourceInfo.HEURISTIC_MIXED;
1092: errors++;
1093: commitnb++;
1094: heuristicnb++;
1095: } catch (HeuristicHazard e) {
1096: TraceTm.jotm.error("Heuristic Hazard");
1097: resinfo.mystate = ResourceInfo.HEURISTIC_HAZARD;
1098: heuristicstate = ResourceInfo.HEURISTIC_HAZARD;
1099: errors++;
1100: commitnb++;
1101: heuristicnb++;
1102: } catch (NotPreparedException e) {
1103: TraceTm.jotm.error("Resource Not Prepared");
1104: resinfo.mystate = ResourceInfo.REGISTERED;
1105: errors++;
1106: } catch (NoSuchObjectException e) {
1107: TraceTm.jotm.error("invalid objref - assume committed");
1108: resinfo.mystate = ResourceInfo.COMMITTED;
1109: commitnb++;
1110: } catch (Exception e) {
1111: TraceTm.jotm.error("exception on commit: ", e);
1112: errors++;
1113: }
1114: }
1115:
1116: if (errors == 0) {
1117: // Everything's fine.
1118: if (TraceTm.jotm.isDebugEnabled()) {
1119: TraceTm.jotm.debug("transaction committed");
1120: }
1121:
1122: mystatus = Status.STATUS_COMMITTED;
1123: mylog.forgetLog();
1124: // Synchronization Objects
1125: do_after_completion();
1126: return 0;
1127: }
1128:
1129: if (heuristicnb == 0) {
1130: // Transaction has been eventually rolled back
1131: TraceTm.jotm.info("transaction rolled back");
1132: mystatus = Status.STATUS_ROLLEDBACK;
1133: mylog.forgetLog();
1134: // Synchronization Objects
1135: do_after_completion();
1136: throw new TransactionRolledbackException();
1137: }
1138:
1139: // Heuristics must be logged
1140: TraceTm.jotm.info("Heuristics must be logged");
1141: mystatus = Status.STATUS_UNKNOWN;
1142: mylog.updateLog();
1143:
1144: // Synchronization Objects
1145: do_after_completion();
1146:
1147: if (report_heuristics) {
1148: switch (heuristicstate) {
1149: case (ResourceInfo.HEURISTIC_ROLLBACK): {
1150: throw new HeuristicRollback();
1151: }
1152: case (ResourceInfo.HEURISTIC_MIXED): {
1153: throw new HeuristicMixed();
1154: }
1155: case (ResourceInfo.HEURISTIC_HAZARD): {
1156: throw new HeuristicHazard();
1157: }
1158: }
1159: }
1160:
1161: return -1;
1162: }
1163:
1164: /**
1165: * Rollbacks the transaction
1166: * This internal routine should be used either for rollback on sub-coordinator
1167: * or for commit on Terminator when prepare returned a vote RollBack.
1168: */
1169: private void do_rollback(boolean report_heuristics)
1170: throws HeuristicMixed {
1171: if (TraceTm.jotm.isDebugEnabled()) {
1172: TraceTm.jotm
1173: .debug("report_heuristics=" + report_heuristics);
1174: }
1175:
1176: mystatus = Status.STATUS_ROLLEDBACK;
1177:
1178: // Rollback resources
1179: int commitnb = 0;
1180:
1181: for (int i = 0; i < resourceList.size(); i++) {
1182: Resource res = (Resource) resourceList.get(i);
1183: try {
1184: if (TraceTm.jotm.isDebugEnabled()) {
1185: TraceTm.jotm.debug("Send rollback to Resource");
1186: }
1187: res.rollback();
1188: } catch (HeuristicCommit e) {
1189: TraceTm.jotm.error("Rollback raised HeuristicCommit");
1190: commitnb++;
1191: } catch (Exception e) {
1192: TraceTm.jotm.error("Cannot rollback resource: ", e);
1193: }
1194: }
1195:
1196: // Synchronization objects
1197: do_after_completion();
1198:
1199: if (commitnb > 0 && report_heuristics) {
1200: // May be should throw HeuristicCommit instead
1201: throw new HeuristicMixed();
1202: }
1203: }
1204:
1205: /**
1206: * Destroys transaction object.
1207: */
1208: private void explicit_destroy() {
1209: if (TraceTm.jotm.isDebugEnabled()) {
1210: TraceTm.jotm.debug("");
1211: }
1212:
1213: // unexportObject explicitly
1214: try {
1215: unexportObject(this );
1216: } catch (Exception e) {
1217: }
1218: }
1219:
1220: /**
1221: * Delay destroy of the ControlImpl object.
1222: * We must keep alive this object a little while for recovery reasons,
1223: * and also in case of Jeremie because we may propagate a reference to
1224: * this object inside the propagation context.
1225: */
1226: private void completed(boolean removeit) {
1227: if (TraceTm.jotm.isDebugEnabled()) {
1228: TraceTm.jotm.debug("removeit=" + removeit);
1229: }
1230:
1231: if (mytimer != null) {
1232: if (removeit) {
1233: // Convert the timer to make a delayed removal of this object
1234: mytimer.change(60, new Integer(2));
1235: } else {
1236: // In case of heuristic, must keep ControlImpl.
1237: // No more timer is needed.
1238: mytimer.unset();
1239: mytimer = null;
1240: }
1241: }
1242: }
1243:
1244: /**
1245: * Sends before_completion to the registered synchronizations
1246: *
1247: * @return 0 if no error
1248: */
1249: private int do_before_completion() {
1250: if (TraceTm.jotm.isDebugEnabled()) {
1251: TraceTm.jotm.debug("ControlImpl.do_before_completion()");
1252: TraceTm.jotm.debug("synchronizationList.size()="
1253: + synchronizationList.size());
1254: }
1255:
1256: int errors = 0;
1257:
1258: for (int i = 0; i < synchronizationList.size(); i++) {
1259:
1260: RemoteSynchro sync = (RemoteSynchro) synchronizationList
1261: .get(i);
1262:
1263: try {
1264: sync.before_completion(this );
1265: } catch (Exception e) {
1266: TraceTm.jotm.error(
1267: "before_completion raised exception ", e);
1268: errors++;
1269: }
1270: }
1271: return errors;
1272: }
1273:
1274: /**
1275: * Sends after_completion to the registered synchronizations
1276: */
1277: private void do_after_completion() {
1278:
1279: if (TraceTm.jotm.isDebugEnabled()) {
1280: TraceTm.jotm.debug("ControlImpl.do_after_completion()");
1281: TraceTm.jotm.debug("status=" + mystatus);
1282: TraceTm.jotm.debug("synchronizationList.size()="
1283: + synchronizationList.size());
1284: }
1285:
1286: for (int i = 0; i < synchronizationList.size(); i++) {
1287: RemoteSynchro sync = (RemoteSynchro) synchronizationList
1288: .get(i);
1289:
1290: try {
1291: sync.after_completion(this , mystatus);
1292: } catch (Exception e) {
1293: TraceTm.jotm.error(
1294: "after_completion raised exception ", e);
1295: }
1296: }
1297: }
1298: }
|