/* * JBoss, Home of Professional Open Source * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. * See the copyright.txt in the distribution for a full listing * of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * (C) 2005-2006, * @author JBoss Inc. */ /* * Copyright (C) 2002, * * Arjuna Technologies Limited, * Newcastle upon Tyne, * Tyne and Wear, * UK. * * $Id: ACCoordinator.java,v 1.5 2005/05/19 12:13:37 nmcl Exp $ */ package com.arjuna.mwlabs.wscf.model.sagas.arjunacore; import com.arjuna.mw.wscf.logging.wscfLogger; import com.arjuna.ats.arjuna.common.Uid; import com.arjuna.ats.arjuna.coordinator.*; import com.arjuna.mw.wscf.model.sagas.participants.*; import com.arjuna.mw.wscf.model.sagas.exceptions.DuplicateSynchronizationException; import com.arjuna.mw.wscf.model.sagas.exceptions.InvalidSynchronizationException; import com.arjuna.mw.wscf.common.Qualifier; import com.arjuna.mw.wscf.common.CoordinatorId; import com.arjuna.mw.wsas.activity.Outcome; import com.arjuna.mw.wsas.completionstatus.CompletionStatus; import com.arjuna.mw.wsas.exceptions.SystemException; import com.arjuna.mw.wsas.exceptions.WrongStateException; import com.arjuna.mw.wsas.exceptions.ProtocolViolationException; import com.arjuna.mw.wscf.exceptions.*; /** * This class represents a specific coordination instance. It inherits from * ArjunaCore TwoPhaseCoordinator so we can do the prepare and commit when a * BA client close is requested and have it roll back if anything goes wrong. * The BA client cancel request maps through to TwoPhaseCoordinator cancel which * also does what we need. Although we inherit synchronization support from * TwoPhaseCoordinator it is not used by the BA code. * * This class also exposes a separate complete method which implements * the deprectaed BA client complete operation allowing coordinator completion * participants to be notified that they complete. This is pretty much redundant * as complete gets called at close anyway. * * @author Mark Little (mark.little@arjuna.com) * @version $Id: ACCoordinator.java,v 1.5 2005/05/19 12:13:37 nmcl Exp $ * @since 1.0. * */ public class BACoordinator extends TwoPhaseCoordinator { private final static int DELISTED = 0; private final static int COMPLETED = 1; private final static int FAILED = 2; public BACoordinator() { super(); _theId = new CoordinatorIdImple(get_uid()); } public BACoordinator(Uid recovery) { super(recovery); _theId = new CoordinatorIdImple(get_uid()); } /** * If the application requires and if the coordination protocol supports it, * then this method can be used to execute a coordination protocol on the * currently enlisted participants at any time prior to the termination of * the coordination scope. * * This implementation only supports coordination at the end of the * activity. * * @exception WrongStateException * Thrown if the coordinator is in a state the does not allow * coordination to occur. * @exception ProtocolViolationException * Thrown if the protocol is violated in some manner during * execution. * @exception SystemException * Thrown if any other error occurs. * * @return The result of executing the protocol, or null. */ public Outcome coordinate (CompletionStatus cs) throws WrongStateException, ProtocolViolationException, SystemException { return null; } /** * ensure all ParticipantCompletion participants have completed and then send a complete message to * any remaining CoordinatorCompletion participants. * @throws WrongStateException if the transaction is neither RUNNING nor PREPARED * @throws SystemException if there are incomplete ParticipantCompletion participants or if one of the * CoordinatorCompletion participants fails to complete. */ public synchronized void complete () throws WrongStateException, SystemException { int status = status(); if (status == ActionStatus.RUNNING) { // check that all ParticipantCompletion participants have completed // throwing a wobbly if not if (pendingList != null) { RecordListIterator iter = new RecordListIterator(pendingList); AbstractRecord absRec = iter.iterate(); while (absRec != null) { if (absRec instanceof ParticipantRecord) { ParticipantRecord pr = (ParticipantRecord) absRec; if (!pr.complete()) { // ok, we must force a rollback preventCommit(); wscfLogger.i18NLogger.warn_model_sagas_arjunacore_BACoordinator_1(get_uid()); throw new SystemException("Participant failed to complete"); } } absRec = iter.iterate(); } } } else { throw new WrongStateException(); } } /** * close the activity * @return the outcome of the close * @throws SystemException */ public int close () throws SystemException { // we need to make sure all coordinator completion aprticipants ahve completed try { complete(); } catch (Exception e) { // carry on as end will catch any further problems } // now call end(). if any of the participant completion participants are not // completed they will throw a WrongStateException during prepare leading to rollback return end(true); } /** * cancel the activity * @return the outcome of the cancel */ public int cancel () { return super.cancel(); } /** * Enrol the specified participant with the coordinator associated with the * current thread. * * @exception WrongStateException * Thrown if the coordinator is not in a state that allows * participants to be enrolled. * @exception DuplicateParticipantException * Thrown if the participant has already been enrolled and * the coordination protocol does not support multiple * entries. * @exception InvalidParticipantException * Thrown if the participant is invalid. * @exception SystemException * Thrown if any other error occurs. */ public void enlistParticipant (Participant act) throws WrongStateException, DuplicateParticipantException, InvalidParticipantException, SystemException { if (act == null) throw new InvalidParticipantException(); AbstractRecord rec = new ParticipantRecord((Participant)act, new Uid()); if (add(rec) != AddOutcome.AR_ADDED) throw new WrongStateException(); else { /* * Presume nothing protocol, so we need to write the intentions list * every time a participant is added. */ } } /** * Remove the specified participant from the coordinator's list. this is the target of the * * @exception InvalidParticipantException * Thrown if the participant is not known of by the * coordinator. * @exception WrongStateException * Thrown if the state of the coordinator does not allow the * participant to be removed * @exception SystemException * Thrown if any other error occurs. */ public synchronized void delistParticipant (String participantId) throws InvalidParticipantException, WrongStateException, SystemException { if (participantId == null) throw new SystemException( wscfLogger.i18NLogger.get_model_sagas_arjunacore_BACoordinator_2()); int status = status(); // exit is only legitimate when the TX is in these states switch (status) { case ActionStatus.RUNNING: case ActionStatus.ABORT_ONLY: changeParticipantStatus(participantId, DELISTED); break; default: throw new WrongStateException( wscfLogger.i18NLogger.get_model_sagas_arjunacore_BACoordinator_3()); } } public synchronized void participantCompleted (String participantId) throws InvalidParticipantException, WrongStateException, SystemException { if (participantId == null) throw new SystemException( wscfLogger.i18NLogger.get_model_sagas_arjunacore_BACoordinator_2()); int status = status(); // completed is only legitimate when the TX is in these states switch (status) { case ActionStatus.ABORTED: break; case ActionStatus.RUNNING: case ActionStatus.ABORT_ONLY: changeParticipantStatus(participantId, COMPLETED); break; default: throw new WrongStateException( wscfLogger.i18NLogger.get_model_sagas_arjunacore_BACoordinator_3()); } } public synchronized void participantFaulted (String participantId) throws InvalidParticipantException, SystemException { if (participantId == null) throw new SystemException( wscfLogger.i18NLogger.get_model_sagas_arjunacore_BACoordinator_2()); int status = status(); // faulted is only legitimate when the TX is in these states switch (status) { case ActionStatus.RUNNING: // if a participant notifies this then we need to mark the transaction as abort only preventCommit(); // !!! deliberate drop through !!! case ActionStatus.ABORT_ONLY: case ActionStatus.COMMITTING: case ActionStatus.COMMITTED: // this can happen during recovery processing case ActionStatus.ABORTING: changeParticipantStatus(participantId, FAILED); break; default: throw new SystemException( wscfLogger.i18NLogger.get_model_sagas_arjunacore_BACoordinator_3()); } } // n.b. this is only appropriate for the 1.1 protocol public synchronized void participantCannotComplete (String participantId) throws InvalidParticipantException, WrongStateException, SystemException { if (participantId == null) throw new SystemException( wscfLogger.i18NLogger.get_model_sagas_arjunacore_BACoordinator_2()); int status = status(); // cannot complete is only legitimate when the TX is in these states switch (status) { case ActionStatus.RUNNING: // if a participant notifies this then we need to mark the transaction as abort only preventCommit(); // !!! deliberate drop through !!! case ActionStatus.ABORT_ONLY: changeParticipantStatus(participantId, DELISTED); break; default: throw new WrongStateException( wscfLogger.i18NLogger.get_model_sagas_arjunacore_BACoordinator_3()); } } /** * Enrol the specified synchronization with the coordinator associated with * the current thread. * * @param act The synchronization to add. * * @exception WrongStateException * Thrown if the coordinator is not in a state that allows * participants to be enrolled. * @exception DuplicateSynchronizationException * Thrown if the participant has already been enrolled and * the coordination protocol does not support multiple * entries. * @exception InvalidSynchronizationException * Thrown if the participant is invalid. * @exception SystemException * Thrown if any other error occurs. */ public void enlistSynchronization (Synchronization act) throws WrongStateException, DuplicateSynchronizationException, InvalidSynchronizationException, SystemException { if (act == null) throw new InvalidSynchronizationException(); SynchronizationRecord rec = new SynchronizationRecord(act, new Uid()); if (addSynchronization(rec) != AddOutcome.AR_ADDED) throw new WrongStateException(); } /** * Remove the specified synchronization from the coordinator's list. * * @exception InvalidSynchronizationException * Thrown if the participant is not known of by the * coordinator. * @exception WrongStateException * Thrown if the state of the coordinator does not allow the * participant to be removed (e.g., in a two-phase protocol * the coordinator is committing.) * @exception SystemException * Thrown if any other error occurs. */ public void delistSynchronization (Synchronization act) throws InvalidSynchronizationException, WrongStateException, SystemException { if (act == null) throw new InvalidSynchronizationException(); else throw new WrongStateException( wscfLogger.i18NLogger.get_model_sagas_arjunacore_BACoordinator_4()); } /** * @exception SystemException * Thrown if any error occurs. * * @return the complete list of qualifiers that have been registered with * the current coordinator. */ public Qualifier[] qualifiers () throws SystemException { return null; } /** * @exception SystemException * Thrown if any error occurs. * * @return The unique identity of the current coordinator. */ public CoordinatorId identifier () throws SystemException { return _theId; } public String type () { return "/StateManager/BasicAction/AtomicAction/Sagas/BACoordinator"; } private final void changeParticipantStatus (String participantId, int status) throws InvalidParticipantException, SystemException { /* * Transaction is active, so we can look at the pendingList only. */ // TODO allow transaction status to be changed during commit - exit // could come in late boolean found = false; if (pendingList != null) { RecordListIterator iter = new RecordListIterator(pendingList); AbstractRecord absRec = iter.iterate(); try { while ((absRec != null) && !found) { if (absRec instanceof ParticipantRecord) { ParticipantRecord pr = (ParticipantRecord) absRec; Participant participant = (Participant) pr.value(); if (participantId.equals(participant.id())) { found = true; if (status == DELISTED) { pr.delist(false); } else if (status == FAILED) { pr.delist(true); } else { pr.completed(); } } } absRec = iter.iterate(); } } catch (Exception ex) { throw new SystemException(ex.toString()); } } if (!found) throw new InvalidParticipantException(); } private CoordinatorIdImple _theId; }