[jboss-svn-commits] JBL Code SVN: r23835 - in labs/jbosstm/trunk/XTS: WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant and 46 other directories.
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Wed Nov 12 06:08:26 EST 2008
Author: adinn
Date: 2008-11-12 06:08:25 -0500 (Wed, 12 Nov 2008)
New Revision: 23835
Added:
labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/
labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantHelper.java
labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantRecoveryRecord.java
labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/PersistableBAParticipant.java
labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryManager.java
labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryModule.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/RecoverableBusinessAgreementWithCoordinatorCompletionParticipant.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/RecoverableBusinessAgreementWithParticipantCompletionParticipant.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/LocalCoordinatorCompletionParticipantStub.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/LocalParticipantCompletionParticipantStub.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/org/jboss/jbossts/xts10/recovery/participant/ba/
labs/jbosstm/trunk/XTS/WS-T/dev/src10/org/jboss/jbossts/xts10/recovery/participant/ba/BAParticipantRecoveryRecord.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/RecoverableBusinessAgreementWithCoordinatorCompletionParticipant.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/RecoverableBusinessAgreementWithParticipantCompletionParticipant.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/org/jboss/jbossts/xts11/recovery/participant/ba/
labs/jbosstm/trunk/XTS/WS-T/dev/src11/org/jboss/jbossts/xts11/recovery/participant/ba/BAParticipantRecoveryRecord.java
labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/exceptions/CancelFailedException.java
labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/participants/RecoverableParticipant.java
labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/participants/RecoverableParticipantWithComplete.java
labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/remote/BARecoveryParticipantManagerImple.java
labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/remote/BARecoveryParticipantManagerImple.java
labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoBARecoveryListener.java
labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoBARecoveryModule.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCBARecoveryListener.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCBARecoveryModule.java
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/BACoordinatorRecoveryModule.java
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/Implementations.java
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/ParticipantRecordSetup.java
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/RecoverACCoordinator.java
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/ba/
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantRecoveryModule.java
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryManagerImple.java
Modified:
labs/jbosstm/trunk/XTS/WS-T/dev/src/com/arjuna/wst/BusinessAgreementWithParticipantCompletionParticipant.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/webservices/wsba/processors/CoordinatorCompletionParticipantProcessor.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/webservices/wsba/processors/ParticipantCompletionParticipantProcessor.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/CoordinatorCompletionCoordinatorProcessorImpl.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/CoordinatorCompletionParticipantProcessorImpl.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/ParticipantCompletionCoordinatorProcessorImpl.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/ParticipantCompletionParticipantProcessorImpl.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/CoordinatorCompletionCoordinatorEngine.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/CoordinatorCompletionParticipantEngine.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/ParticipantCompletionCoordinatorEngine.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/ParticipantCompletionParticipantEngine.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/BusinessAgreementWithCoordinatorCompletionStub.java
labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/BusinessAgreementWithParticipantCompletionStub.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/webservices11/wsba/processors/CoordinatorCompletionParticipantProcessor.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/webservices11/wsba/processors/ParticipantCompletionParticipantProcessor.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/CoordinatorCompletionCoordinatorProcessorImpl.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/CoordinatorCompletionParticipantProcessorImpl.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/ParticipantCompletionCoordinatorProcessorImpl.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/ParticipantCompletionParticipantProcessorImpl.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/CoordinatorCompletionCoordinatorEngine.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/CoordinatorCompletionParticipantEngine.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/ParticipantCompletionCoordinatorEngine.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/ParticipantCompletionParticipantEngine.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/stub/BusinessAgreementWithCoordinatorCompletionStub.java
labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/stub/BusinessAgreementWithParticipantCompletionStub.java
labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst/tests/junit/TestCoordinatorCompletionParticipantProcessor.java
labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst/tests/junit/TestParticipantCompletionParticipantProcessor.java
labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst11/tests/junit/TestCoordinatorCompletionParticipantProcessor.java
labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst11/tests/junit/TestParticipantCompletionParticipantProcessor.java
labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/api/CoordinatorManager.java
labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/ACCoordinator.java
labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/CoordinatorControl.java
labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/CoordinatorServiceImple.java
labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/ParticipantRecord.java
labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/twophase/arjunacore/ACCoordinator.java
labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/twophase/arjunacore/ParticipantRecord.java
labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/RegistrarImple.java
labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/local/LocalRegistrarImple.java
labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/participants/BusinessAgreementWithCoordinatorCompletionImple.java
labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/participants/BusinessAgreementWithParticipantCompletionImple.java
labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/participants/BusinessAgreementWithCoordinatorCompletionImple.java
labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/participants/BusinessAgreementWithParticipantCompletionImple.java
labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/remote/BAParticipantManagerImple.java
labs/jbosstm/trunk/XTS/demo/dd/jboss/service-web-app.xml
labs/jbosstm/trunk/XTS/demo/ddrpc/jboss/service-web-app.xml
labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantManager.java
labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantParticipantBA.java
labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantServiceBA.java
labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiManager.java
labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiParticipantBA.java
labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiServiceBA.java
labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreManager.java
labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreParticipantBA.java
labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreServiceBA.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantManager.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantParticipantBA.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantServiceBA.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiManager.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiParticipantBA.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiServiceBA.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreManager.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreParticipantBA.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreServiceBA.java
labs/jbosstm/trunk/XTS/docs/XTSBARecoveryNotes.odt
labs/jbosstm/trunk/XTS/docs/XTSBARecoveryNotes.pdf
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/XTSService.java
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/ACCoordinatorRecoveryModule.java
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/ParticipantRecordSetup.java
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/RecoverACCoordinator.java
labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/at/ATParticipantRecoveryModule.java
Log:
initial version of WSBA 1.0 and 1.1 recovery including mods to JaxWS and JaxRPC based demos. note this does not yet correctly detect participant failures when there are multiple retries of recovered transactions -- fixes for JBTM-404
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src/com/arjuna/wst/BusinessAgreementWithParticipantCompletionParticipant.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src/com/arjuna/wst/BusinessAgreementWithParticipantCompletionParticipant.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src/com/arjuna/wst/BusinessAgreementWithParticipantCompletionParticipant.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -47,7 +47,7 @@
* completed.
*/
- public void cancel () throws WrongStateException, SystemException;
+ public void cancel () throws FaultedException, WrongStateException, SystemException;
/**
* The transaction has cancelled. The participant previously
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantHelper.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantHelper.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantHelper.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,57 @@
+package org.jboss.jbossts.xts.recovery.participant.ba;
+
+import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
+
+import java.io.Serializable;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+
+import org.jboss.jbossts.xts.recovery.participant.ba.PersistableBAParticipant;
+
+/**
+ * helper to support retrieval of durable BA participant recovery state from participant
+ */
+public class BAParticipantHelper
+{
+ /**
+ * obtain a byte array containing the recovery state associated with the supplied
+ * participant.
+ * @param useSerialization true if the object should be converted to a byte array using
+ * serialization otherwise it will be converted by casting to the PersistableBAParticipant
+ * interface and employing the getRecoveryState method.
+ * @param participant the participant whose recovery state is to be obtained
+ * @return the state to be saved ro null if no state needs to be saved
+ * @throws Exception an exception occurred generating the required recoverable state
+ */
+ public static byte[] getRecoveryState(boolean useSerialization, BusinessAgreementWithParticipantCompletionParticipant participant)
+ throws Exception
+ {
+ if (useSerialization) {
+ // serialize the object to a byte array via an object output stream
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
+ final ObjectOutputStream oos = new ObjectOutputStream(baos) ;
+ oos.writeObject(participant) ;
+ oos.flush() ;
+ return baos.toByteArray();
+ } else {
+ PersistableBAParticipant persistableParticipant = (PersistableBAParticipant) participant;
+ return persistableParticipant.getRecoveryState();
+ }
+ }
+
+ /**
+ * return true if the object can be saved and restored using serialization otherwise
+ * return false
+ * @param participant
+ * @return
+ */
+ public static boolean isSerializable(BusinessAgreementWithParticipantCompletionParticipant participant)
+ {
+ if (participant instanceof Serializable) {
+ return true;
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantRecoveryRecord.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantRecoveryRecord.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantRecoveryRecord.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,227 @@
+package org.jboss.jbossts.xts.recovery.participant.ba;
+
+import com.arjuna.wst.PersistableParticipant;
+import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
+import com.arjuna.ats.arjuna.state.OutputObjectState;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+import com.arjuna.webservices.logging.WSTLogger;
+
+import javax.xml.stream.XMLStreamException;
+import java.io.*;
+
+/**
+ * asbstract class used to implement save, recover and reactivate API for durable
+ * XTS participants. this is subclassed by both a 1.0 and a 1.1 specific class because
+ * the activate operation needs to create a participant engine appropriate to the
+ * protocol in use when the participant was saved.
+ */
+public abstract class BAParticipantRecoveryRecord implements PersistableParticipant {
+
+ /**
+ * construct the protocol-independent part of a WS-BA participant recovery record
+ * @param id
+ * @param participant
+ */
+ protected BAParticipantRecoveryRecord(String id, BusinessAgreementWithParticipantCompletionParticipant participant, boolean isParticipantCompletion)
+ {
+ this.id = id;
+ this.participant = participant;
+ recoveryState = null;
+ recoveryStateValid = false;
+ this.isParticipantCompletion = isParticipantCompletion;
+ }
+
+ /**
+ * Retrieve and save the state of the particpant to the specified input object stream.
+ * @param oos The output output stream.
+ * @return true if persisted, false otherwise.
+ *
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.saveState_1 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.saveState_1] Could not save recovery state for non-serializable WS-BA participant {0}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.saveState_2 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.saveState_2] XML stream exception saving recovery state for participant {0}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.saveState_3 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.saveState_3] I/O exception saving recovery state for participant {0}
+ */
+ public final boolean saveState(OutputObjectState oos) {
+ if (participant == null) {
+ return false;
+ }
+
+ try {
+ useSerialization = BAParticipantHelper.isSerializable(participant);
+ recoveryState = BAParticipantHelper.getRecoveryState(useSerialization, participant);
+ recoveryStateValid = true;
+ } catch (Exception exception) {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.saveState_1", new Object[] {id}) ;
+ }
+ // if we continue here then we cannot recover this transaction if we crash during
+ // commit processing. we should strictly fail here to play safe but . . .
+
+ recoveryStateValid = false;
+ }
+
+ try {
+ oos.packString(id);
+ saveEndpointReference(oos);
+ oos.packBoolean(recoveryStateValid);
+ oos.packBoolean(isParticipantCompletion);
+ if (recoveryStateValid) {
+ oos.packBoolean(useSerialization);
+ if (recoveryState != null) {
+ oos.packBoolean(true);
+ oos.packBytes(recoveryState);
+ } else {
+ oos.packBoolean(false);
+ }
+ }
+ } catch (XMLStreamException xmle) {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.saveState_2", new Object[] {id}, xmle) ;
+ }
+ return false;
+ } catch (IOException ioe) {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.saveState_3", new Object[] {id}, ioe) ;
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Restore the state of the particpant from the specified input object stream.
+ *
+ * @param ios The Input object stream.
+ * @return true if restored, false otherwise.
+ *
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.restoreState_1 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.restoreState_1] XML stream exception restoring recovery state for participant {0}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.restoreState_2 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.restoreState_2] I/O exception saving restoring state for participant {0}
+ */
+ public boolean restoreState(InputObjectState ios) {
+ try {
+ id = ios.unpackString();
+ restoreEndpointReference(ios);
+ recoveryStateValid = ios.unpackBoolean();
+ isParticipantCompletion = ios.unpackBoolean();
+ if (recoveryStateValid) {
+ useSerialization = ios.unpackBoolean();
+ if (ios.unpackBoolean()) {
+ recoveryState = ios.unpackBytes();
+ } else {
+ recoveryState = null;
+ }
+ }
+ } catch (XMLStreamException xmle) {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.restoreState_1", new Object[] {id}, xmle) ;
+ }
+ return false;
+ } catch (IOException ioe) {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.restoreState_2", new Object[] {id}, ioe) ;
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * called during recovery processing to attempt to convert the restored application-
+ * specific recovery state back into a participant
+ * @param module the XTS recovery module to be used to attempt the conversion
+ * @return
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.restoreParticipant_1 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.restoreParticipant_1] participant {0} has no saved recovery state to recover
+ */
+
+ public boolean restoreParticipant(XTSBARecoveryModule module) throws Exception
+ {
+ if (participant != null) {
+ // don't think this should ever happen
+ return false;
+ }
+
+ if (recoveryStateValid) {
+ if (useSerialization) {
+ final ByteArrayInputStream bais = new ByteArrayInputStream(recoveryState) ;
+ final ObjectInputStream ois = new ObjectInputStream(bais) ;
+
+ if (isParticipantCompletion) {
+ participant = module.deserializeParticipantCompletionParticipant(getId(), ois);
+ } else {
+ participant = module.deserializeCoordinatorCompletionParticipant(getId(), ois);
+ }
+ } else {
+ if (isParticipantCompletion) {
+ participant = module.recreateParticipantCompletionParticipant(getId(), recoveryState);
+ } else {
+ participant = module.recreateCoordinatorCompletionParticipant(getId(), recoveryState);
+ }
+ }
+
+ if (participant != null) {
+ return true;
+ }
+ } else {
+ // the original participant did not provide a way to save its state so
+ // throw an exception to notify this
+
+ String mesg = WSTLogger.arjLoggerI18N.getString("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryRecord.restoreParticipant_1", id);
+
+ throw new Exception(mesg);
+ }
+
+ return false;
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * return the path string under which BA participant records are to be located in the TX
+ * object store
+ * @return
+ */
+ public static String type ()
+ {
+ return type;
+ }
+
+ /**
+ * save the endpoint reference to the coordinator for this participant
+ */
+ protected abstract void saveEndpointReference(OutputObjectState oos) throws IOException, XMLStreamException;
+
+ /**
+ * restore the endpoint reference to the coordinator for this participant
+ */
+ protected abstract void restoreEndpointReference(InputObjectState ios) throws IOException, XMLStreamException;
+
+ /**
+ * create a participant engine to manage commit or rollback processing for the
+ * participant and install it in the active participants table
+ */
+ public abstract void activate();
+
+ /**
+ * test whether a participant is currently activated with the id of this recovery record.
+ *
+ * @return true if a participant is currently activated with the id of this recovery record
+ */
+ public abstract boolean isActive();
+
+ protected BusinessAgreementWithParticipantCompletionParticipant participant;
+ protected String id;
+ private boolean useSerialization;
+ private byte[] recoveryState;
+ private boolean recoveryStateValid;
+ protected boolean isParticipantCompletion;
+ final private static String type = "/XTS/WSBA/ParticipantRecoveryRecord";
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/PersistableBAParticipant.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/PersistableBAParticipant.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/PersistableBAParticipant.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,11 @@
+package org.jboss.jbossts.xts.recovery.participant.ba;
+
+/**
+ * an interface which can be implemented by BA participants which wish to save recovery state at prepare
+ * which they can subsequently use to restore the participant state if a crash happnes bteween prepare and
+ * commit/rollback
+ */
+public interface PersistableBAParticipant
+{
+ byte[] getRecoveryState() throws Exception;
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryManager.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryManager.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,146 @@
+package org.jboss.jbossts.xts.recovery.participant.ba;
+
+import com.arjuna.ats.arjuna.common.Uid;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Abstract class defining API for managing both participant and coordinator recovery
+ * On the participant this is responsible for saving WS-BA participant recovery records
+ * during prepare, deleting them at commit and recreating and reactivating them during
+ * recovery.
+ * On the coordinator side it currently merely records whether coordinator recovery has
+ * completed its first scan
+ */
+public abstract class XTSBARecoveryManager {
+ /*****************************************************************************************/
+ /* get or set the BA recovery manager singleton */
+ /*****************************************************************************************/
+
+ /**
+ * obtain a reference to the WS-BA recovery manager singleton instance
+ * @return the singleton instance
+ */
+ public static XTSBARecoveryManager getRecoveryManager()
+ {
+ return theRecoveryManager;
+ }
+
+ /**
+ * set the WS-BA recovery manager singleton instance
+ * @param recoveryManager the instance to use as the recovery manager
+ * @return the singleton previously in use or null if it was not previously set
+ */
+ public static XTSBARecoveryManager setRecoveryManager(XTSBARecoveryManager recoveryManager)
+ {
+ XTSBARecoveryManager old = theRecoveryManager;
+ theRecoveryManager = recoveryManager;
+
+ return old;
+ }
+
+ /*****************************************************************************************/
+ /* API used by application client code to register XTS recovery modules at application */
+ /* startup and to unregister them when the application is unloaded */
+ /*****************************************************************************************/
+
+ /**
+ * register an application specific recovery module which acts as a helper to recreate
+ * a WS-BA durable participant from the participant's recovery data saved at prepare
+ * @param module the module which will be used to identify and recreate participants
+ * for the application
+ * @throws NullPointerException if the supplied module is null
+ */
+ public abstract void registerRecoveryModule(XTSBARecoveryModule module);
+
+ /**
+ * unregister an application specific recovery module previously registered as
+ * a helper to recretae WS-BA durable participants
+ * @param module the module to be unregistered
+ * @throws java.util.NoSuchElementException if the module is not currently registered
+ */
+ public abstract void unregisterRecoveryModule(XTSBARecoveryModule module) throws NoSuchElementException;
+
+ /*****************************************************************************************/
+ /* API used by participant service to create and delete participant recovery records in */
+ /* persistent store during normal operation */
+ /*****************************************************************************************/
+
+ /**
+ * save the supplied participant recovery record to persistent storage
+ * @param participantRecoveryRecord
+ */
+ public abstract boolean writeParticipantRecoveryRecord(BAParticipantRecoveryRecord participantRecoveryRecord);
+
+ /**
+ * remove any participant recovery record with the supplied id from persistent storage
+ * @param id
+ */
+ public abstract boolean deleteParticipantRecoveryRecord(String id);
+
+ /*****************************************************************************************/
+ /* API used by recovery scanning thread to add entries to the BA recovery manager's */
+ /* table of recovered participant recovery records. */
+ /*****************************************************************************************/
+
+ /**
+ * test whether the supplied uid identifies an active participant or a recovered but inactive
+ * participant
+ * @param uid
+ */
+ public abstract boolean isParticipantPresent(Uid uid);
+
+ /**
+ * add a recovered participant record to the table of unrecovered participants which
+ * need to be recreated following recovery
+ *
+ * @param uid the uid under which the participant was saved in the file store
+ * @param participantRecoveryRecord the in-memory represenattion of the recovery record
+ * saved to disk
+ */
+ public abstract void addParticipantRecoveryRecord(Uid uid, BAParticipantRecoveryRecord participantRecoveryRecord);
+
+ /**
+ * see if a participant recovery record with a given id exists in the table of unrecovered
+ * participants
+ * @param id the identifier of the participant being sought
+ * @return the participant recovery record with the supplied id or null if it is not found
+ */
+ public abstract BAParticipantRecoveryRecord findParticipantRecoveryRecord(String id);
+
+ /**
+ * process all entries in the recovered participant map and attempt to recreate the
+ * application participant and activate it
+ */
+ public abstract void recoverParticipants();
+
+ /**
+ * test whether the first BA participant recovery scan has completed. this indicates whether
+ * there may or may not still be unknown participant recovery records on disk. If the first
+ * scan has not yet completed then a commit or rollback message for an unknown participant
+ * must be dropped. If it has then a commit or rollback for an unknown participant must be
+ * acknowledged with, respectively, a committed or aborted message.
+ */
+ public abstract boolean isParticipantRecoveryStarted();
+
+ /**
+ * test whether the first BA coordinator recovery scan has completed. this indicates whether
+ * there may or may not still be unknown BA transcation records on disk. If the first
+ * scan has not yet completed then a prepare message for an unknown participant
+ * must be dropped. If it has then a perpare for an unknown participant must be
+ * acknowledged with a rollback message.
+ */
+ public abstract boolean isCoordinatorRecoveryStarted();
+
+ /**
+ * record the fact thatwhether the first BA coordinator recovery scan has completed.
+ */
+
+ public abstract void setCoordinatorRecoveryStarted();
+
+ /**
+ * the singleton instance of the recovery manager
+ */
+
+ private static XTSBARecoveryManager theRecoveryManager = null;
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryModule.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryModule.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryModule.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,74 @@
+package org.jboss.jbossts.xts.recovery.participant.ba;
+
+import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
+import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
+
+import java.io.ObjectInputStream;
+
+/**
+ * an interface implemented by applications which wish to be involved in recovering
+ * saved participants after a crash.
+ */
+public interface XTSBARecoveryModule
+{
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and recreate the participant by deserializing
+ * it from the supplied object input stream. n.b. this is only appropriate in case the
+ * original was a ParticipantCompletion participant saved using serialization.
+ * @param id the id used when the participant was created
+ * @param stream a stream from which the application should deserialise the participant
+ * if it recognises that the id belongs to the module's application
+ * @return the deserialized ParticipantCompletion participant
+ * @throws Exception if an error occurs deserializing the ParticipantCompletion participant
+ */
+ public BusinessAgreementWithParticipantCompletionParticipant
+ deserializeParticipantCompletionParticipant(String id, ObjectInputStream stream) throws Exception;
+
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and use the saved recovery state to recreate the
+ * participant. n.b. this is only appropriate in case the original was a ParticipantCompletion
+ * participant saved after being converted to a byte array using the PersistibleBAParticipant
+ * interface.
+ * @param id the id used when the participant was created
+ * @param recoveryState a byte array returned form the original participant via a call to
+ * method getRecoveryState of interface PersistableBAParticipant
+ * @return the recreated ParticipantCompletion participant
+ * @throws Exception if an error occurs converting the recoveryState back to a
+ * ParticipantCompletion participant
+ */
+ public BusinessAgreementWithParticipantCompletionParticipant
+ recreateParticipantCompletionParticipant(String id, byte[] recoveryState) throws Exception;
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and recreate the participant by deserializing
+ * it from the supplied object input stream. n.b. this is only appropriate in case the
+ * original was a CoordinatorCompletion participant saved using serialization.
+ * @param id the id used when the participant was created
+ * @param stream a stream from which the application should deserialise the participant
+ * if it recognises that the id belongs to the module's application
+ * @return the deserialized ParticipantCompletion participant
+ * @throws Exception if an error occurs deserializing the CoordinatorCompletion participant
+ */
+ public BusinessAgreementWithCoordinatorCompletionParticipant
+ deserializeCoordinatorCompletionParticipant(String id, ObjectInputStream stream) throws Exception;
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and use the saved recovery state to recreate the
+ * participant. n.b. this is only appropriate in case the original was a CoordinatorCompletion
+ * participant saved after being converted to a byte array using the PersistibleBAParticipant
+ * interface.
+ * @param id the id used when the participant was created
+ * @param recoveryState a byte array returned form the original participant via a call to
+ * method getRecoveryState of interface PersistableBAParticipant
+ * @return the recreated ParticipantCompletion participant
+ * @throws Exception if an error occurs converting the recoveryState back to a
+ * CoordinatorCompletion participant
+ */
+ public BusinessAgreementWithCoordinatorCompletionParticipant
+ recreateCoordinatorCompletionParticipant(String id, byte[] recoveryState) throws Exception;
+}
\ No newline at end of file
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/webservices/wsba/processors/CoordinatorCompletionParticipantProcessor.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/webservices/wsba/processors/CoordinatorCompletionParticipantProcessor.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/webservices/wsba/processors/CoordinatorCompletionParticipantProcessor.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -74,6 +74,12 @@
public abstract void deactivateParticipant(final CoordinatorCompletionParticipantInboundEvents participant) ;
/**
+ * Check whether a participant with the given id is currently active
+ * @param identifier The identifier.
+ */
+ public abstract boolean isActive(final String identifier) ;
+
+ /**
* Cancel.
* @param cancel The cancel notification.
* @param addressingContext The addressing context.
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/webservices/wsba/processors/ParticipantCompletionParticipantProcessor.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/webservices/wsba/processors/ParticipantCompletionParticipantProcessor.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/webservices/wsba/processors/ParticipantCompletionParticipantProcessor.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -74,6 +74,12 @@
public abstract void deactivateParticipant(final ParticipantCompletionParticipantInboundEvents participant) ;
/**
+ * Check whether a participant with the given id is currently active
+ * @param identifier The identifier.
+ */
+ public abstract boolean isActive(final String identifier) ;
+
+ /**
* Cancel.
* @param cancel The cancel notification.
* @param addressingContext The addressing context.
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/RecoverableBusinessAgreementWithCoordinatorCompletionParticipant.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/RecoverableBusinessAgreementWithCoordinatorCompletionParticipant.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/RecoverableBusinessAgreementWithCoordinatorCompletionParticipant.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,41 @@
+/*
+ * 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, 2003, 2004,
+ *
+ * Arjuna Technologies Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: BusinessAgreementWithCoordinatorCompletionParticipant.java,v 1.1.2.1 2004/05/26 10:02:33 nmcl Exp $
+ */
+
+package com.arjuna.wst;
+
+/**
+ * extension of BusinessAgreementWithCoordinatorCompletionParticipant API which allows establishment of a
+ * back channel from the coordinator side protocol engine to the coordinator.
+ */
+
+public interface RecoverableBusinessAgreementWithCoordinatorCompletionParticipant extends RecoverableBusinessAgreementWithParticipantCompletionParticipant, BusinessAgreementWithCoordinatorCompletionParticipant
+{
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/RecoverableBusinessAgreementWithParticipantCompletionParticipant.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/RecoverableBusinessAgreementWithParticipantCompletionParticipant.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/RecoverableBusinessAgreementWithParticipantCompletionParticipant.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,47 @@
+/*
+ * 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, 2003, 2004,
+ *
+ * Arjuna Technologies Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: BusinessAgreementWithParticipantCompletionParticipant.java,v 1.1.2.1 2004/05/26 10:02:33 nmcl Exp $
+ */
+
+package com.arjuna.wst;
+
+/**
+ * extension of BusinessAgreementWithCoordinatorCompletionParticipant API which allows establishment of a
+ * back channel from the coordinator side protocol engine to the coordinator.
+ */
+
+public interface RecoverableBusinessAgreementWithParticipantCompletionParticipant extends BusinessAgreementWithParticipantCompletionParticipant
+{
+ /**
+ * establish a back channel from the coordinator side protocol engine to the coordinator.
+ *
+ * @param participantManager a manager which will forward incoming remote participant requests to the coordinator
+ */
+ public void setParticipantManager(BAParticipantManager participantManager);
+}
\ No newline at end of file
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/CoordinatorCompletionCoordinatorProcessorImpl.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/CoordinatorCompletionCoordinatorProcessorImpl.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/CoordinatorCompletionCoordinatorProcessorImpl.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -33,6 +33,7 @@
import com.arjuna.webservices.wsba.client.CoordinatorCompletionParticipantClient;
import com.arjuna.webservices.wsba.processors.CoordinatorCompletionCoordinatorProcessor;
import com.arjuna.wsc.messaging.MessageId;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
/**
@@ -186,6 +187,7 @@
*
* @message com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_1 [com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_1] - Unexpected exception thrown from completed:
* @message com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_2 [com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_2] - Completed called on unknown coordinator: {0}
+ * @message com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_3 [com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_3] - Ignoring completed called on unidentified coordinator until recovery pass is complete: {0}
*/
public void completed(final NotificationType completed, final AddressingContext addressingContext,
final ArjunaContext arjunaContext)
@@ -209,7 +211,13 @@
}
else if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_2", new Object[] {instanceIdentifier}) ;
+ if (areRecoveryLogEntriesAccountedFor()) {
+ // this is a resend for a lost participant
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_2", new Object[] {instanceIdentifier}) ;
+ } else {
+ // this may be a resend for a participant still pending recovery
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_3", new Object[] {instanceIdentifier}) ;
+ }
}
}
@@ -259,6 +267,7 @@
*
* @message com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fault_1 [com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fault_1] - Unexpected exception thrown from fault:
* @message com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fault_2 [com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fault_2] - Fault called on unknown coordinator: {0}
+ * @message com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fault_3 [com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fault_3] - Ignoring fault called on unidentified coordinator until recovery pass is complete: {0}
*/
public void fault(final ExceptionType fault, final AddressingContext addressingContext,
final ArjunaContext arjunaContext)
@@ -280,7 +289,7 @@
}
}
}
- else
+ else if (areRecoveryLogEntriesAccountedFor())
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
@@ -288,6 +297,13 @@
}
sendFaulted(addressingContext, arjunaContext) ;
}
+ else
+ {
+ if (WSTLogger.arjLoggerI18N.isDebugEnabled())
+ {
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fault_3", new Object[] {instanceIdentifier}) ;
+ }
+ }
}
/**
@@ -444,4 +460,15 @@
}
}
}
+
+ /**
+ * Tests if there may be unknown coordinator entries in the recovery log.
+ *
+ * @return false if there may be unknown coordinator entries in the recovery log.
+ */
+
+ private static boolean areRecoveryLogEntriesAccountedFor()
+ {
+ return XTSBARecoveryManager.getRecoveryManager().isCoordinatorRecoveryStarted();
+ }
}
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/CoordinatorCompletionParticipantProcessorImpl.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/CoordinatorCompletionParticipantProcessorImpl.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/CoordinatorCompletionParticipantProcessorImpl.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -63,8 +63,17 @@
{
activatedObjectProcessor.deactivateObject(participant) ;
}
-
+
/**
+ * Check whether a participant with the given id is currently active
+ *
+ * @param identifier The identifier.
+ */
+ public boolean isActive(String identifier) {
+ return activatedObjectProcessor.getObject(identifier) != null;
+ }
+
+ /**
* Get the participant with the specified identifier.
* @param instanceIdentifier The participant identifier.
* @return The participant or null if not known.
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/ParticipantCompletionCoordinatorProcessorImpl.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/ParticipantCompletionCoordinatorProcessorImpl.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/ParticipantCompletionCoordinatorProcessorImpl.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -33,6 +33,7 @@
import com.arjuna.webservices.wsba.client.ParticipantCompletionParticipantClient;
import com.arjuna.webservices.wsba.processors.ParticipantCompletionCoordinatorProcessor;
import com.arjuna.wsc.messaging.MessageId;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
/**
@@ -186,6 +187,7 @@
*
* @message com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_1 [com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_1] - Unexpected exception thrown from completed:
* @message com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_2 [com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_2] - Completed called on unknown coordinator: {0}
+ * @message com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_3 [com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_3] - Ignoring completed called on unidentified coordinator until recovery pass is complete: {0}
*/
public void completed(final NotificationType completed, final AddressingContext addressingContext,
final ArjunaContext arjunaContext)
@@ -209,7 +211,13 @@
}
else if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_2", new Object[] {instanceIdentifier}) ;
+ if (areRecoveryLogEntriesAccountedFor()) {
+ // this is a resend for a lost participant
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_2", new Object[] {instanceIdentifier}) ;
+ } else {
+ // this may be a resend for a participant still pending recovery
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.ws1.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_3", new Object[] {instanceIdentifier}) ;
+ }
}
}
@@ -259,6 +267,7 @@
*
* @message com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_1 [com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_1] - Unexpected exception thrown from fault:
* @message com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_2 [com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_2] - Fault called on unknown coordinator: {0}
+ * @message com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_3 [com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_3] - Ignoring fault called on unidentified coordinator until recovery pass is complete: {0}
*/
public void fault(final ExceptionType fault, final AddressingContext addressingContext,
final ArjunaContext arjunaContext)
@@ -280,7 +289,7 @@
}
}
}
- else
+ else if (areRecoveryLogEntriesAccountedFor())
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
@@ -288,6 +297,14 @@
}
sendFaulted(addressingContext, arjunaContext) ;
}
+ else
+ {
+ // we must delay responding until we can be sure there is no participant pending recovery
+ if (WSTLogger.arjLoggerI18N.isDebugEnabled())
+ {
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_3", new Object[] {instanceIdentifier}) ;
+ }
+ }
}
/**
@@ -444,4 +461,15 @@
}
}
}
+
+ /**
+ * Tests if there may be unknown coordinator entries in the recovery log.
+ *
+ * @return false if there may be unknown coordinator entries in the recovery log.
+ */
+
+ private static boolean areRecoveryLogEntriesAccountedFor()
+ {
+ return XTSBARecoveryManager.getRecoveryManager().isCoordinatorRecoveryStarted();
+ }
}
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/ParticipantCompletionParticipantProcessorImpl.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/ParticipantCompletionParticipantProcessorImpl.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/ParticipantCompletionParticipantProcessorImpl.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -63,8 +63,17 @@
{
activatedObjectProcessor.deactivateObject(participant) ;
}
-
+
/**
+ * Check whether a participant with the given id is currently active
+ *
+ * @param identifier The identifier.
+ */
+ public boolean isActive(String identifier) {
+ return activatedObjectProcessor.getObject(identifier) != null;
+ }
+
+ /**
* Get the participant with the specified identifier.
* @param instanceIdentifier The participant identifier.
* @return The participant or null if not known.
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/CoordinatorCompletionCoordinatorEngine.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/CoordinatorCompletionCoordinatorEngine.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/CoordinatorCompletionCoordinatorEngine.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -66,15 +66,19 @@
* The current state.
*/
private State state ;
-
/**
+ * The flag indicating that this coordinator has been recovered from the log.
+ */
+ private boolean recovered ;
+
+ /**
* Construct the initial engine for the coordinator.
* @param id The coordinator id.
* @param participant The participant endpoint reference.
*/
public CoordinatorCompletionCoordinatorEngine(final String id, final EndpointReferenceType participant)
{
- this(id, participant, State.STATE_ACTIVE) ;
+ this(id, participant, State.STATE_ACTIVE, false) ;
}
/**
@@ -84,12 +88,13 @@
* @param state The initial state.
*/
public CoordinatorCompletionCoordinatorEngine(final String id, final EndpointReferenceType participant,
- final State state)
+ final State state, final boolean recovered)
{
this.id = id ;
this.instanceIdentifier = new InstanceIdentifier(id) ;
this.participant = participant ;
this.state = state ;
+ this.recovered = recovered;
}
/**
@@ -99,7 +104,13 @@
public void setCoordinator(final BAParticipantManager coordinator)
{
this.coordinator = coordinator ;
- CoordinatorCompletionCoordinatorProcessor.getProcessor().activateCoordinator(this, id) ;
+ // unrecovered participants are always activated
+ // we only need to reactivate recovered participants which were successfully COMPLETED or which began
+ // CLOSING. any others will only have been saved because of a heuristic outcome. we can safely drop
+ // it since we implement presumed abort.
+ if (!recovered || state == State.STATE_COMPLETED || state == State.STATE_CLOSING) {
+ CoordinatorCompletionCoordinatorProcessor.getProcessor().activateCoordinator(this, id) ;
+ }
}
/**
@@ -495,7 +506,35 @@
sendComplete() ;
}
- return waitForState(State.STATE_COMPLETING, TransportTimer.getTransportTimeout()) ;
+ waitForState(State.STATE_COMPLETING, TransportTimer.getTransportTimeout()) ;
+
+ synchronized(this)
+ {
+ if (state != State.STATE_COMPLETING)
+ {
+ // if this is a recovered participant then forget will not have
+ // deactivated the entry so that this (recovery) thread can
+ // detect it and update its log entry. so we need to deactivate
+ // the entry here.
+
+ if (recovered) {
+ CoordinatorCompletionCoordinatorProcessor.getProcessor().deactivateCoordinator(this) ;
+ }
+
+ return state ;
+ }
+
+ // the participant is still uncommitted so it will be rewritten to the log.
+ // it remains activated in case a committed message comes in between now and
+ // the next scan. the recovery code will detect this active participant when
+ // rescanning the log and use it instead of recreating a new one.
+ // we need to mark this one as recovered so it does not get deleted until
+ // the next scan
+
+ recovered = true;
+
+ return State.STATE_COMPLETING;
+ }
}
/**
@@ -829,7 +868,18 @@
private void ended()
{
changeState(State.STATE_ENDED) ;
- CoordinatorCompletionCoordinatorProcessor.getProcessor().deactivateCoordinator(this) ;
+ // participants which have not been recovered from the log can be deactivated now.
+
+ // participants which have been recovered are left for the recovery thread to deactivate.
+ // this is because the recovery thread may have timed out waiting for a response to
+ // the commit message and gone on to complete its scan and suspend. the next scan
+ // will detect this activated participant and note that it has ended. if a crash
+ // happens in between the recovery thread can safely recreate and reactivate the
+ // participant and resend the close since the close/closed exchange is idempotent.
+
+ if (!recovered) {
+ CoordinatorCompletionCoordinatorProcessor.getProcessor().deactivateCoordinator(this) ;
+ }
}
/**
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/CoordinatorCompletionParticipantEngine.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/CoordinatorCompletionParticipantEngine.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/CoordinatorCompletionParticipantEngine.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -39,6 +39,8 @@
import com.arjuna.wsc.messaging.MessageId;
import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
import com.arjuna.wst.FaultedException;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
+import org.jboss.jbossts.xts10.recovery.participant.ba.BAParticipantRecoveryRecord;
/**
* The coordinator completion participant state engine
@@ -70,8 +72,17 @@
* The associated timer task or null.
*/
private TimerTask timerTask ;
-
/**
+ * true id this is a recovered participant otherwise false.
+ */
+ private boolean recovered ;
+
+ /**
+ * true if this participant's recovery details have been logged to disk otherwise false
+ */
+ private boolean persisted;
+
+ /**
* Construct the initial engine for the participant.
* @param id The participant id.
* @param coordinator The coordinator endpoint reference.
@@ -80,7 +91,7 @@
public CoordinatorCompletionParticipantEngine(final String id, final EndpointReferenceType coordinator,
final BusinessAgreementWithCoordinatorCompletionParticipant participant)
{
- this(id, coordinator, participant, State.STATE_ACTIVE) ;
+ this(id, coordinator, participant, State.STATE_ACTIVE, false) ;
}
/**
@@ -91,13 +102,15 @@
* @param state The initial state.
*/
public CoordinatorCompletionParticipantEngine(final String id, final EndpointReferenceType coordinator,
- final BusinessAgreementWithCoordinatorCompletionParticipant participant, final State state)
+ final BusinessAgreementWithCoordinatorCompletionParticipant participant, final State state, final boolean recovered)
{
this.id = id ;
this.instanceIdentifier = new InstanceIdentifier(id) ;
this.coordinator = coordinator ;
this.participant = participant ;
this.state = state ;
+ this.recovered = recovered;
+ this.persisted = recovered;
}
/**
@@ -134,10 +147,14 @@
}
}
- if ((current == State.STATE_ACTIVE) || (current == State.STATE_COMPLETING))
+ if (current == State.STATE_ACTIVE)
{
- executeCancel() ;
+ executeCancel(false) ;
}
+ else if (current == State.STATE_COMPLETING)
+ {
+ executeCancel(true) ;
+ }
else if (current == State.STATE_COMPLETED)
{
sendCompleted() ;
@@ -360,19 +377,33 @@
* Faulting-Compensating -> Ended
* Exiting -> Exiting (invalid state)
* Ended -> Ended
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.faulted_1 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.faulted_1] - Unable to delete recovery record during faulted for WS-BA participant {0}
*/
public void faulted(final NotificationType faulted, final AddressingContext addressingContext, final ArjunaContext arjunaContext)
{
final State current ;
+ boolean deleteRequired = false;
synchronized(this)
{
current = state ;
if ((current == State.STATE_FAULTING) || (current == State.STATE_FAULTING_ACTIVE) ||
(current == State.STATE_FAULTING_COMPENSATING))
{
+ deleteRequired = persisted;
ended() ;
}
}
+ // if we just ended the participant ensure any log record gets deleted
+
+ if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- nothing more we can do than log a message
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.faulted_1", new Object[] {id}) ;
+ }
+ }
+ }
}
/**
@@ -409,15 +440,31 @@
* @param soapFault The soap fault.
* @param addressingContext The addressing context.
* @param arjunaContext The arjuna context.
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.soapFault_1 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.soapFault_1] - Unable to delete recovery record during soapFault processing for WS-BA participant {0}
*/
public void soapFault(final SoapFault soapFault, final AddressingContext addressingContext, final ArjunaContext arjunaContext)
{
- ended() ;
- try
- {
- participant.error() ;
- }
- catch (final Throwable th) {} // ignore
+ boolean deleteRequired;
+ synchronized(this) {
+ deleteRequired = persisted;
+ ended() ;
+ }
+ // TODO -- clarify when and why this gets called and update doc in interface. also check unknown()
+ try
+ {
+ participant.error() ;
+ }
+ catch (final Throwable th) {} // ignore
+ // if we just ended the participant ensure any log record gets deleted
+ if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- nothing more we can do than log a message
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.soapFault_1", new Object[] {id}) ;
+ }
+ }
+ }
}
/**
@@ -434,10 +481,14 @@
* Faulting-Compensating -> Faulting-Compensating (invalid state)
* Exiting -> Exiting (invalid state)
* Ended -> Ended (invalid state)
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.completed_1 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.completed_1] - Unable to write recovery record during completed for WS-BA participant {0}
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.completed_2 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.completed_2] - Unable to delete recovery record during completed for WS-BA participant {0}
*/
public State completed()
{
- final State current ;
+ State current ;
+ boolean faultRequired = false;
+ boolean deleteRequired = false;
synchronized(this)
{
current = state ;
@@ -446,10 +497,55 @@
changeState(State.STATE_COMPLETED) ;
}
}
-
- if ((current == State.STATE_ACTIVE) || (current == State.STATE_COMPLETING) ||
- (current == State.STATE_COMPLETED))
+ if (current == State.STATE_COMPLETING) {
+ // ok we need to write the participant details to disk because it has just completed
+ BAParticipantRecoveryRecord recoveryRecord = new BAParticipantRecoveryRecord(id, participant, true, coordinator);
+
+ if (!XTSBARecoveryManager.getRecoveryManager().writeParticipantRecoveryRecord(recoveryRecord)) {
+ // hmm, could not write entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.completed_1", new Object[] {id}) ;
+ }
+ // we need to fail this transaction
+ faultRequired = true;
+ }
+ }
+ // recheck state before we decide whether we need to fail -- we might have been sent a cancel while
+ // writing the log
+
+ synchronized(this)
{
+ current = state ;
+ if (current == State.STATE_COMPLETED) {
+ if (!faultRequired) {
+ // record the fact that we have persisted this object so later operations will delete
+ // the log record
+ persisted = true;
+ } else {
+ // we must force a fault but we don't have a log record to delete
+ changeState(State.STATE_FAULTING_ACTIVE);
+ }
+ } else {
+ // we need to delete the log record here as the cancel would not have known it was persisted
+ deleteRequired = true;
+ }
+ }
+
+ // check to see if we need to send a fail or delete the log record before going ahead to complete
+
+ if (faultRequired) {
+ current = fault();
+ } else if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.completed_2", new Object[] {id}) ;
+ }
+ }
+ } else if ((current == State.STATE_COMPLETING) || (current == State.STATE_COMPLETED))
+ {
sendCompleted() ;
}
@@ -778,10 +874,16 @@
/**
* Execute the cancel transition.
*
- * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1] - Unexpected exception from participant cancel
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1] - Unexpected exception from participant cancel for WS-BA participant {0}
*/
- private void executeCancel()
+ private void executeCancel(boolean duringComplete)
{
+ boolean faultRequired = false;
+
+ // TODO -- there is a potential race here with a completing thread
+ // the state diagrams in the spec say that if a cancel comes in while completing we have to cancel
+ // but the participant may be part way through executing a complete. strictly, that's something
+ // the participant has to deal with not us
try
{
participant.cancel() ;
@@ -790,18 +892,39 @@
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1", new Object[] {id}, th) ;
}
- return ;
+ /*
+ * we can get here from state ACTIVE or CONPLETING. we could roll back the state as though the cancel
+ * never happened. the only problem is that coming from state COMPLETING we don't know whether the
+ * completing thread has logged its state or logged it and then deleted it because it saw the transition
+ * to CANCELING. so we roll back to ACTIVE but fail if we have come from COMPLETING
+ */
+ synchronized (this) {
+ if (state == State.STATE_CANCELING) {
+ if (duringComplete) {
+ faultRequired = true;
+ changeState(State.STATE_FAULTING_ACTIVE);
+ } else {
+ changeState(State.STATE_ACTIVE);
+ return ;
+ }
+ }
+ }
}
- sendCancelled() ;
- ended() ;
+ if (faultRequired) {
+ fault();
+ } else {
+ sendCancelled() ;
+ ended() ;
+ }
}
/**
* Execute the close transition.
*
- * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_1 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_1] - Unexpected exception from participant close
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_1 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_1] - Unexpected exception from participant close for WS-BA participant {0}
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_2 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_2] - Unable to delete recovery record during close for WS-BA participant {0}
*/
private void executeClose()
{
@@ -813,10 +936,34 @@
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_1", new Object[] {id}, th) ;
}
+ // restore previous state so we can retry the close otherwise we get stuck in state closing forever
+ changeState(State.STATE_COMPLETED);
+
+ sendCompleted();
return ;
}
+ // delete any log record for the participant
+ if (persisted) {
+ // if we cannot delete the participant record we effectively drop the close message
+ // here in the hope that we have better luck next time..
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- leave it so we can maybe retry later
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_2", new Object[] {id}) ;
+ }
+ // restore previous state so we can retry the close otherwise we get stuck in state closing forever
+
+ changeState(State.STATE_COMPLETED);
+
+ sendCompleted();
+
+ return;
+ }
+ }
+
sendClosed() ;
ended() ;
}
@@ -824,7 +971,9 @@
/**
* Execute the compensate transition.
*
- * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1] - Unexpected exception from participant compensate
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1] - Faulted exception from participant compensate for WS-BA participant {0}
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_2 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_2] - Unexpected exception from participant compensate for WS-BA participant {0}
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_3 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_3] - Unable to delete recovery record during compensate for WS-BA participant {0}
*/
private void executeCompensate()
{
@@ -834,6 +983,11 @@
}
catch (final FaultedException fe)
{
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1", new Object[] {id}, fe);
+ }
+ // fault here because the aprticipant doesn't want to retry the compensate
fault() ;
}
catch (final Throwable th)
@@ -854,21 +1008,41 @@
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_2", th) ;
}
return ;
}
final State current ;
+ boolean faultRequired = false;
synchronized (this)
{
current = state ;
+ // need to do this while synchronized so no fail calls can get in on between
+
if (current == State.STATE_COMPENSATING)
{
- ended() ;
+ if (persisted) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // we have to fail since we don't want to run the compensate method again
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_3", new Object[] {id}) ;
+ }
+ faultRequired = true;
+ changeState(State.STATE_FAULTING_COMPENSATING);
+ }
+ }
+ // if we did not fail then we can decommission the participant now avoiding any further races
+ // we will send the compensate after we exit the synchronized block
+ if (!faultRequired) {
+ ended();
+ }
}
}
- if (current == State.STATE_COMPENSATING)
+ if (faultRequired) {
+ fault();
+ } else if (current == State.STATE_COMPENSATING)
{
sendCompensated() ;
}
@@ -877,7 +1051,8 @@
/**
* Execute the complete transition.
*
- * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1] - Unexpected exception from participant compensate
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1] - Unexpected exception from participant complete for WS-BA parfticipant {0}
+ * @message com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_2 [com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_2] - Unable to write log record during participant complete for WS-BA parfticipant {0}
*/
private void executeComplete()
{
@@ -889,12 +1064,14 @@
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1", new Object[] {id}, th) ;
}
return ;
}
- final State current ;
+ State current ;
+ boolean faultRequired = false;
+ boolean deleteRequired = false;
synchronized (this)
{
current = state ;
@@ -905,6 +1082,57 @@
}
if (current == State.STATE_COMPLETING)
{
+ // ok we need to write the participant details to disk because it has just completed
+ BAParticipantRecoveryRecord recoveryRecord = new BAParticipantRecoveryRecord(id, participant, true, coordinator);
+
+ if (!XTSBARecoveryManager.getRecoveryManager().writeParticipantRecoveryRecord(recoveryRecord)) {
+ // hmm, could not write entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeComplete_2", new Object[] {id}) ;
+ }
+ // we need to fail this transaction
+ faultRequired = true;
+ }
+ }
+
+ // recheck state before we decide whether we need to fail -- we might have been sent a cancel while
+ // writing the log
+
+ synchronized(this)
+ {
+ current = state ;
+ if (current == State.STATE_COMPLETED) {
+ if (!faultRequired) {
+ // record the fact that we have persisted this object so later operations will delete
+ // the log record
+ persisted = true;
+ } else {
+ // we must force a fail but we don't have a log record to delete
+ changeState(State.STATE_FAULTING_ACTIVE);
+ }
+ } else {
+ // we cannot force a fail now so just delete
+ faultRequired = false;
+ // we need to delete the log record here as the cancel would not have known it was persisted
+ deleteRequired = true;
+ }
+ }
+
+ // check to see if we need to send a fail or delete the log record before going ahead to complete
+
+ if (faultRequired) {
+ current = fault();
+ } else if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.completed_2", new Object[] {id}) ;
+ }
+ }
+ } else if (current == State.STATE_COMPLETING || current == State.STATE_COMPLETED)
+ {
sendCompleted() ;
}
}
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/ParticipantCompletionCoordinatorEngine.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/ParticipantCompletionCoordinatorEngine.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/ParticipantCompletionCoordinatorEngine.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -65,15 +65,19 @@
* The current state.
*/
private State state ;
-
/**
+ * The flag indicating that this coordinator has been recovered from the log.
+ */
+ private boolean recovered ;
+
+ /**
* Construct the initial engine for the coordinator.
* @param id The coordinator id.
* @param participant The participant endpoint reference.
*/
public ParticipantCompletionCoordinatorEngine(final String id, final EndpointReferenceType participant)
{
- this(id, participant, State.STATE_ACTIVE) ;
+ this(id, participant, State.STATE_ACTIVE, false) ;
}
/**
@@ -83,12 +87,13 @@
* @param state The initial state.
*/
public ParticipantCompletionCoordinatorEngine(final String id, final EndpointReferenceType participant,
- final State state)
+ final State state, final boolean recovered)
{
this.id = id ;
this.instanceIdentifier = new InstanceIdentifier(id) ;
this.participant = participant ;
this.state = state ;
+ this.recovered = recovered;
}
/**
@@ -98,7 +103,13 @@
public void setCoordinator(final BAParticipantManager coordinator)
{
this.coordinator = coordinator ;
- ParticipantCompletionCoordinatorProcessor.getProcessor().activateCoordinator(this, id) ;
+ // unrecovered participants are always activated
+ // we only need to reactivate recovered participants which were successfully COMPLETED or which began
+ // CLOSING. any others will only have been saved because of a heuristic outcome. we can safely drop
+ // it since we implement presumed abort.
+ if (!recovered || state == State.STATE_COMPLETED || state == State.STATE_CLOSING) {
+ ParticipantCompletionCoordinatorProcessor.getProcessor().activateCoordinator(this, id) ;
+ }
}
/**
@@ -456,7 +467,34 @@
sendClose() ;
}
- return waitForState(State.STATE_CLOSING, TransportTimer.getTransportTimeout()) ;
+ waitForState(State.STATE_CLOSING, TransportTimer.getTransportTimeout()) ;
+ synchronized(this)
+ {
+ if (state != State.STATE_CLOSING)
+ {
+ // if this is a recovered participant then forget will not have
+ // deactivated the entry so that this (recovery) thread can
+ // detect it and update its log entry. so we need to deactivate
+ // the entry here.
+
+ if (recovered) {
+ ParticipantCompletionCoordinatorProcessor.getProcessor().deactivateCoordinator(this) ;
+ }
+
+ return state ;
+ }
+
+ // the participant is still uncommitted so it will be rewritten to the log.
+ // it remains activated in case a committed message comes in between now and
+ // the next scan. the recovery code will detect this active participant when
+ // rescanning the log and use it instead of recreating a new one.
+ // we need to mark this one as recovered so it does not get deleted until
+ // the next scan
+
+ recovered = true;
+
+ return State.STATE_CLOSING;
+ }
}
/**
@@ -745,7 +783,18 @@
private void ended()
{
changeState(State.STATE_ENDED) ;
- ParticipantCompletionCoordinatorProcessor.getProcessor().deactivateCoordinator(this) ;
+ // participants which have not been recovered from the log can be deactivated now.
+
+ // participants which have been recovered are left for the recovery thread to deactivate.
+ // this is because the recovery thread may have timed out waiting for a response to
+ // the commit message and gone on to complete its scan and suspend. the next scan
+ // will detect this activated participant and note that it has completed. if a crash
+ // happens in between the recovery thread can safely recreate and reactivate the
+ // participant and resend the commit since the commit/committed exchange is idempotent.
+
+ if (!recovered) {
+ ParticipantCompletionCoordinatorProcessor.getProcessor().deactivateCoordinator(this) ;
+ }
}
/**
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/ParticipantCompletionParticipantEngine.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/ParticipantCompletionParticipantEngine.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/messaging/engines/ParticipantCompletionParticipantEngine.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -38,6 +38,8 @@
import com.arjuna.wsc.messaging.MessageId;
import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
import com.arjuna.wst.FaultedException;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
+import org.jboss.jbossts.xts10.recovery.participant.ba.BAParticipantRecoveryRecord;
/**
* The participant completion participant state engine
@@ -69,8 +71,17 @@
* The associated timer task or null.
*/
private TimerTask timerTask ;
-
/**
+ * true id this is a recovered participant otherwise false.
+ */
+ private boolean recovered ;
+
+ /**
+ * true if this participant's recovery details have been logged to disk otherwise false
+ */
+ private boolean persisted;
+
+ /**
* Construct the initial engine for the participant.
* @param id The participant id.
* @param coordinator The coordinator endpoint reference.
@@ -79,7 +90,7 @@
public ParticipantCompletionParticipantEngine(final String id, final EndpointReferenceType coordinator,
final BusinessAgreementWithParticipantCompletionParticipant participant)
{
- this(id, coordinator, participant, State.STATE_ACTIVE) ;
+ this(id, coordinator, participant, State.STATE_ACTIVE, false) ;
}
/**
@@ -90,13 +101,15 @@
* @param state The initial state.
*/
public ParticipantCompletionParticipantEngine(final String id, final EndpointReferenceType coordinator,
- final BusinessAgreementWithParticipantCompletionParticipant participant, final State state)
+ final BusinessAgreementWithParticipantCompletionParticipant participant, final State state, final boolean recovered)
{
this.id = id ;
this.instanceIdentifier = new InstanceIdentifier(id) ;
this.coordinator = coordinator ;
this.participant = participant ;
this.state = state ;
+ this.recovered = recovered;
+ this.persisted = recovered;
}
/**
@@ -302,19 +315,33 @@
* Faulting-Compensating -> Ended
* Exiting -> Exiting (invalid state)
* Ended -> Ended
+ * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.faulted_1 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.faulted_1] - Unable to delete recovery record during faulted for WS-BA participant {0}
*/
public void faulted(final NotificationType faulted, final AddressingContext addressingContext, final ArjunaContext arjunaContext)
{
final State current ;
+ boolean deleteRequired = false;
synchronized(this)
{
current = state ;
if ((current == State.STATE_FAULTING) || (current == State.STATE_FAULTING_ACTIVE) ||
(current == State.STATE_FAULTING_COMPENSATING))
{
+ deleteRequired = persisted;
ended() ;
}
}
+ // if we just ended the participant ensure any log record gets deleted
+
+ if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- nothing more we can do than log a message
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.faulted_1", new Object[] {id}) ;
+ }
+ }
+ }
}
/**
@@ -351,15 +378,31 @@
* @param soapFault The soap fault.
* @param addressingContext The addressing context.
* @param arjunaContext The arjuna context.
+ * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.soapFault_1 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.soapFault_1] - Unable to delete recovery record during soapFault processing for WS-BA participant {0}
*/
public void soapFault(final SoapFault soapFault, final AddressingContext addressingContext, final ArjunaContext arjunaContext)
{
- ended() ;
- try
- {
- participant.error() ;
- }
- catch (final Throwable th) {} // ignore
+ boolean deleteRequired;
+ synchronized(this) {
+ deleteRequired = persisted;
+ ended() ;
+ }
+ // TODO -- clarify when and why this gets called and update doc in interface. also check unknown()
+ try
+ {
+ participant.error() ;
+ }
+ catch (final Throwable th) {} // ignore
+ // if we just ended the participant ensure any log record gets deleted
+ if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- nothing more we can do than log a message
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.soapFault_1", new Object[] {id}) ;
+ }
+ }
+ }
}
/**
@@ -375,10 +418,14 @@
* Faulting-Compensating -> Faulting-Compensating (invalid state)
* Exiting -> Exiting (invalid state)
* Ended -> Ended (invalid state)
+ * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.completed_1 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.completed_1] - Unable to write recovery record during completed for WS-BA participant {0}
+ * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.completed_2 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.completed_2] - Unable to delete recovery record during completed for WS-BA participant {0}
*/
public State completed()
{
- final State current ;
+ State current ;
+ boolean faultRequired = false;
+ boolean deleteRequired = false;
synchronized(this)
{
current = state ;
@@ -388,8 +435,57 @@
}
}
- if ((current == State.STATE_ACTIVE) || (current == State.STATE_COMPLETED))
+ if (current == State.STATE_ACTIVE) {
+ // ok we need to write the participant details to disk because it has just completed
+ BAParticipantRecoveryRecord recoveryRecord = new BAParticipantRecoveryRecord(id, participant, true, coordinator);
+
+ if (!XTSBARecoveryManager.getRecoveryManager().writeParticipantRecoveryRecord(recoveryRecord)) {
+ // hmm, could not write entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.completed_1", new Object[] {id}) ;
+ }
+ // we need to fail this transaction
+ faultRequired = true;
+ }
+ }
+ // recheck state before we decide whether we need to fail -- we might have been sent a cancel while
+ // writing the log
+
+ synchronized(this)
{
+ current = state ;
+ if (current == State.STATE_COMPLETED) {
+ if (!faultRequired) {
+ // record the fact that we have persisted this object so later operations will delete
+ // the log record
+ persisted = true;
+ } else {
+ // we must force a fail but we don't have a log record to delete
+ changeState(State.STATE_FAULTING_ACTIVE);
+ }
+ } else {
+ // we cannot force a fail now so just delete
+ faultRequired = false;
+ // we need to delete the log record here as the cancel would not have known it was persisted
+ deleteRequired = true;
+ }
+ }
+
+ // check to see if we need to send a fail or delete the log record before going ahead to complete
+
+ if (faultRequired) {
+ current = fault();
+ } else if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.completed_2", new Object[] {id}) ;
+ }
+ }
+ } else if ((current == State.STATE_ACTIVE) || (current == State.STATE_COMPLETED))
+ {
sendCompleted() ;
}
@@ -714,7 +810,7 @@
/**
* Execute the cancel transition.
*
- * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1] - Unexpected exception from participant cancel
+ * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1] - Unexpected exception from participant cancel fro WS-BA participant {0}
*/
private void executeCancel()
{
@@ -726,8 +822,17 @@
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1", new Object[] { id}, th) ;
}
+ /*
+ * we only get here in from state ACTIVE so if we are still in state CANCELING then roll back the
+ * state allowing a retry of the cancel
+ */
+ synchronized (this) {
+ if (state == State.STATE_CANCELING) {
+ changeState(State.STATE_ACTIVE);
+ }
+ }
return ;
}
sendCancelled() ;
@@ -737,7 +842,8 @@
/**
* Execute the close transition.
*
- * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1] - Unexpected exception from participant close
+ * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1] - Unexpected exception from participant close for WS-BA participant {0}
+ * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_2 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_2] - Unable to delete recovery record during close for WS-BA participant {0}
*/
private void executeClose()
{
@@ -749,10 +855,33 @@
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1", new Object[] {id}, th) ;
}
+ // restore previous state so we can retry the close otherwise we get stuck in state closing forever
+ changeState(State.STATE_COMPLETED);
+
+ sendCompleted();
return ;
}
+ // delete any log record for the participant
+ if (persisted) {
+ // if we cannot delete the participant record we effectively drop the close message
+ // here in the hope that we have better luck next time..
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- leave it so we can maybe retry later
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_2", new Object[] {id}) ;
+ }
+ // restore previous state so we can retry the close otherwise we get stuck in state closing forever
+
+ changeState(State.STATE_COMPLETED);
+
+ sendCompleted();
+
+ return;
+ }
+ }
sendClosed() ;
ended() ;
}
@@ -760,7 +889,9 @@
/**
* Execute the compensate transition.
*
- * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_1 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_1] - Unexpected exception from participant compensate
+ * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_1 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_1] - Faulted exception from participant compensate for WS-BA participant {0}
+ * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_2 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_2] - Unexpected exception from participant compensate for WS-BA participant {0}
+ * @message com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_3 [com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_3] - Unable to delete recovery record during compensate for WS-BA participant {0}
*/
private void executeCompensate()
{
@@ -770,7 +901,13 @@
}
catch (final FaultedException fe)
{
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_2", new Object[] {id}, fe);
+ }
+ // fault here because the participant doesn't want to retry the compensate
fault() ;
+ return;
}
catch (final Throwable th)
{
@@ -790,21 +927,41 @@
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_2", new Object[] {id}, th) ;
}
return ;
}
final State current ;
+ boolean faultRequired = false;
synchronized (this)
{
current = state ;
+ // need to do this while synchronized so no fail calls can get in on between
+
if (current == State.STATE_COMPENSATING)
{
- ended() ;
+ if (persisted) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // we have to fail since we don't want to run the compensate method again
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_3", new Object[] {id}) ;
+ }
+ faultRequired = true;
+ changeState(State.STATE_FAULTING_COMPENSATING);
+ }
+ }
+ // if we did not fail then we can decommission the participant now avoiding any further races
+ // we will send the compensate after we exit the synchronized block
+ if (!faultRequired) {
+ ended();
+ }
}
}
- if (current == State.STATE_COMPENSATING)
+ if (faultRequired) {
+ fault();
+ } else if (current == State.STATE_COMPENSATING)
{
sendCompensated() ;
}
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/BusinessAgreementWithCoordinatorCompletionStub.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/BusinessAgreementWithCoordinatorCompletionStub.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/BusinessAgreementWithCoordinatorCompletionStub.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -27,9 +27,11 @@
package com.arjuna.wst.stub;
import java.io.StringWriter;
+import java.io.StringReader;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamWriter;
+import javax.xml.stream.XMLStreamReader;
import com.arjuna.ats.arjuna.state.InputObjectState;
import com.arjuna.ats.arjuna.state.OutputObjectState;
@@ -37,14 +39,11 @@
import com.arjuna.webservices.soap.SoapUtils;
import com.arjuna.webservices.util.StreamHelper;
import com.arjuna.webservices.wsba.State;
-import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
-import com.arjuna.wst.FaultedException;
-import com.arjuna.wst.PersistableParticipant;
-import com.arjuna.wst.SystemException;
-import com.arjuna.wst.WrongStateException;
+import com.arjuna.webservices.wsaddr.EndpointReferenceType;
+import com.arjuna.wst.*;
import com.arjuna.wst.messaging.engines.CoordinatorCompletionCoordinatorEngine;
-public class BusinessAgreementWithCoordinatorCompletionStub implements BusinessAgreementWithCoordinatorCompletionParticipant, PersistableParticipant
+public class BusinessAgreementWithCoordinatorCompletionStub implements RecoverableBusinessAgreementWithCoordinatorCompletionParticipant, PersistableParticipant
{
private static final QName QNAME_BACC_PARTICIPANT = new QName("baccParticipant") ;
private CoordinatorCompletionCoordinatorEngine participant ;
@@ -55,6 +54,13 @@
this.participant = participant ;
}
+ /**
+ * default constructor for use during recovery
+ */
+ public BusinessAgreementWithCoordinatorCompletionStub ()
+ {
+ this.participant = null ;
+ }
public synchronized void close ()
throws WrongStateException, SystemException
{
@@ -215,8 +221,17 @@
StreamHelper.writeEndElement(writer, null, null) ;
writer.close() ;
- oos.packString(writer.toString()) ;
+ oos.packString(sw.toString()) ;
+ final State state = participant.getStatus();
+ final QName stateName = state.getValue();
+ final String ns = stateName.getNamespaceURI();
+ final String localPart = stateName.getLocalPart();
+ final String prefix = stateName.getPrefix();
+ oos.packString(ns != null ? ns : "");
+ oos.packString(localPart != null ? localPart : "");
+ oos.packString(prefix != null ? prefix : "");
+
return true ;
}
catch (final Throwable th)
@@ -231,25 +246,41 @@
*/
public boolean restoreState(final InputObjectState ios)
{
- // KEV - rework
- return false ;
-// try
-// {
-// final String id = ios.unpackString() ;
-// final String eprValue = ios.unpackString() ;
-//
-// final XMLStreamReader reader = SoapUtils.getXMLStreamReader(new StringReader(eprValue)) ;
-// StreamHelper.checkNextStartTag(reader, QNAME_BACC_PARTICIPANT) ;
-// final EndpointReferenceType endpointReferenceType = new EndpointReferenceType(reader) ;
-//
-// _id = id ;
-// _businessAgreementWithCoordinatorCompletionParticipant = endpointReferenceType ;
-// return true ;
-// }
-// catch (final Throwable th)
-// {
-// WSTLogger.arjLoggerI18N.error("com.arjuna.wst.stub.BusinessAgreementWithCoordinatorCompletionStub_3", th) ;
-// return false ;
-// }
+ try
+ {
+ final String id = ios.unpackString();
+ final String eprValue = ios.unpackString();
+
+ final XMLStreamReader reader = SoapUtils.getXMLStreamReader(new StringReader(eprValue)) ;
+ StreamHelper.checkNextStartTag(reader, QNAME_BACC_PARTICIPANT) ;
+ final EndpointReferenceType endpointReferenceType = new EndpointReferenceType(reader) ;
+ String ns = ios.unpackString();
+ final String localPart = ios.unpackString();
+ String prefix = ios.unpackString();
+ if ("".equals(ns)) {
+ ns = null;
+ }
+ if ("".equals(prefix)) {
+ prefix = null;
+ }
+
+ QName statename = new QName(ns, localPart, prefix);
+ State state = State.toState(statename);
+
+ participant = new CoordinatorCompletionCoordinatorEngine(id, endpointReferenceType, state, true);
+ return true ;
+ } catch (final Throwable th) {
+ WSTLogger.arjLoggerI18N.error("com.arjuna.wst.stub.BusinessAgreementWithCoordinatorCompletionStub_3", th) ;
+ return false ;
+ }
}
+
+ /**
+ * establish a back channel from the coordinator side protocol engine to the coordinator.
+ *
+ * @param participantManager a manager which will forward incoming remote participant requests to the coordinator
+ */
+ public void setParticipantManager(BAParticipantManager participantManager) {
+ participant.setCoordinator(participantManager);
+ }
}
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/BusinessAgreementWithParticipantCompletionStub.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/BusinessAgreementWithParticipantCompletionStub.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/BusinessAgreementWithParticipantCompletionStub.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -27,9 +27,11 @@
package com.arjuna.wst.stub;
import java.io.StringWriter;
+import java.io.StringReader;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamWriter;
+import javax.xml.stream.XMLStreamReader;
import com.arjuna.ats.arjuna.state.InputObjectState;
import com.arjuna.ats.arjuna.state.OutputObjectState;
@@ -37,14 +39,11 @@
import com.arjuna.webservices.soap.SoapUtils;
import com.arjuna.webservices.util.StreamHelper;
import com.arjuna.webservices.wsba.State;
-import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
-import com.arjuna.wst.FaultedException;
-import com.arjuna.wst.PersistableParticipant;
-import com.arjuna.wst.SystemException;
-import com.arjuna.wst.WrongStateException;
+import com.arjuna.webservices.wsaddr.EndpointReferenceType;
+import com.arjuna.wst.*;
import com.arjuna.wst.messaging.engines.ParticipantCompletionCoordinatorEngine;
-public class BusinessAgreementWithParticipantCompletionStub implements BusinessAgreementWithParticipantCompletionParticipant, PersistableParticipant
+public class BusinessAgreementWithParticipantCompletionStub implements RecoverableBusinessAgreementWithParticipantCompletionParticipant, PersistableParticipant
{
private static final QName QNAME_BAPC_PARTICIPANT = new QName("bapcParticipant") ;
@@ -56,6 +55,14 @@
this.participant = participant ;
}
+ /**
+ * default constructor for use during recovery
+ */
+ public BusinessAgreementWithParticipantCompletionStub ()
+ {
+ this.participant = null ;
+ }
+
public synchronized void close ()
throws WrongStateException, SystemException
{
@@ -175,8 +182,17 @@
StreamHelper.writeEndElement(writer, null, null) ;
writer.close() ;
- oos.packString(writer.toString()) ;
+ oos.packString(sw.toString()) ;
+ final State state = participant.getStatus();
+ final QName stateName = state.getValue();
+ final String ns = stateName.getNamespaceURI();
+ final String localPart = stateName.getLocalPart();
+ final String prefix = stateName.getPrefix();
+ oos.packString(ns != null ? ns : "");
+ oos.packString(localPart != null ? localPart : "");
+ oos.packString(prefix != null ? prefix : "");
+
return true ;
}
catch (final Throwable th)
@@ -191,25 +207,43 @@
*/
public boolean restoreState(final InputObjectState ios)
{
- // KEV - rework
- return false ;
-// try
-// {
-// final String id = ios.unpackString() ;
-// final String eprValue = ios.unpackString() ;
-//
-// final XMLStreamReader reader = SoapUtils.getXMLStreamReader(new StringReader(eprValue)) ;
-// StreamHelper.checkNextStartTag(reader, QNAME_BAPC_PARTICIPANT) ;
-// final EndpointReferenceType endpointReferenceType = new EndpointReferenceType(reader) ;
-//
-// _id = id ;
-// _businessAgreementWithParticipantCompletionParticipant = endpointReferenceType ;
-// return true ;
-// }
-// catch (final Throwable th)
-// {
-// WSTLogger.arjLoggerI18N.error("com.arjuna.wst.stub.BusinessAgreementWithParticipantCompletionStub_3", th) ;
-// return false ;
-// }
+ try
+ {
+ final String id = ios.unpackString();
+ final String eprValue = ios.unpackString();
+
+ final XMLStreamReader reader = SoapUtils.getXMLStreamReader(new StringReader(eprValue)) ;
+ StreamHelper.checkNextStartTag(reader, QNAME_BAPC_PARTICIPANT) ;
+ final EndpointReferenceType endpointReferenceType = new EndpointReferenceType(reader) ;
+ String ns = ios.unpackString();
+ final String localPart = ios.unpackString();
+ String prefix = ios.unpackString();
+ if ("".equals(ns)) {
+ ns = null;
+ }
+ if ("".equals(prefix)) {
+ prefix = null;
+ }
+
+ QName statename = new QName(ns, localPart, prefix);
+ State state = State.toState(statename);
+
+ participant = new ParticipantCompletionCoordinatorEngine(id, endpointReferenceType, state, true);
+ return true ;
+ }
+ catch (final Throwable th)
+ {
+ WSTLogger.arjLoggerI18N.error("com.arjuna.wst.stub.BusinessAgreementWithParticipantCompletionStub_3", th) ;
+ return false ;
+ }
}
+
+ /**
+ * establish a back channel from the coordinator side protocol engine to the coordinator.
+ *
+ * @param participantManager a manager which will forward incoming remote participant requests to the coordinator
+ */
+ public void setParticipantManager(BAParticipantManager participantManager) {
+ participant.setCoordinator(participantManager);
+ }
}
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/LocalCoordinatorCompletionParticipantStub.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/LocalCoordinatorCompletionParticipantStub.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/LocalCoordinatorCompletionParticipantStub.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,67 @@
+package com.arjuna.wst.stub;
+
+import com.arjuna.wst.*;
+import com.arjuna.ats.arjuna.state.OutputObjectState;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+
+/**
+ * wrapper class allowing an application participant to be registered directly with the local coordinator service.
+ * this extends the local participant completion wrapper to also provide the complete method.
+ */
+public class LocalCoordinatorCompletionParticipantStub
+ extends LocalParticipantCompletionParticipantStub
+ implements RecoverableBusinessAgreementWithCoordinatorCompletionParticipant, PersistableParticipant {
+
+ public LocalCoordinatorCompletionParticipantStub(BusinessAgreementWithCoordinatorCompletionParticipant participant, String id) {
+ super(participant, id);
+ // keep a handle on the participant as a coordinatorCompletion participant so we can call its
+ // complete method without having to cast it
+
+ this.coordinatorCompletionParticipant = participant;
+ }
+
+ // empty constructor for crash recovery
+
+ public LocalCoordinatorCompletionParticipantStub()
+ {
+ super();
+ this.coordinatorCompletionParticipant = null;
+ }
+
+ /**
+ * The coordinator is informing the participant that all work it needs to
+ * do within the scope of this business activity has been received.
+ */
+
+ public void complete() throws WrongStateException, SystemException {
+ coordinatorCompletionParticipant.complete();
+ }
+
+ /**
+ * Save the state of the particpant to the specified input object stream.
+ *
+ * @param oos The output output stream.
+ * @return true if persisted, false otherwise.
+ */
+ public boolean saveState(OutputObjectState oos) {
+
+ return super.saveState(oos);
+ }
+
+ /**
+ * Restore the state of the particpant from the specified input object stream.
+ *
+ * @param ios The Input object stream.
+ * @return true if restored, false otherwise.
+ */
+ public boolean restoreState(InputObjectState ios) {
+
+ boolean result = super.restoreState(ios);
+
+ coordinatorCompletionParticipant = (BusinessAgreementWithCoordinatorCompletionParticipant) participant;
+
+ return false;
+ }
+
+ private BusinessAgreementWithCoordinatorCompletionParticipant coordinatorCompletionParticipant;
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/LocalParticipantCompletionParticipantStub.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/LocalParticipantCompletionParticipantStub.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/com/arjuna/wst/stub/LocalParticipantCompletionParticipantStub.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,152 @@
+package com.arjuna.wst.stub;
+
+import com.arjuna.wst.*;
+import com.arjuna.ats.arjuna.state.OutputObjectState;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+
+/**
+ * wrapper class allowing an application participant to be registered directly with the local coordinator service.
+ * obvioulsy this wrapper cuts out the network hop delivering messgaes from coordinator to participant and vice
+ * versa. it also differs from the remote stub in two further respects. firstly, it saves and restores the
+ * participant details embedded directly in the coordinator (transaction) log record rather than in a separate
+ * participant record. this still requires use of an application registered helper module to recreate the
+ * participant from its saved state. secondly, it does not currently propagate the participant manager details
+ * to the underlying participant.
+ */
+public class LocalParticipantCompletionParticipantStub
+ implements RecoverableBusinessAgreementWithParticipantCompletionParticipant, PersistableParticipant
+{
+ public LocalParticipantCompletionParticipantStub(BusinessAgreementWithParticipantCompletionParticipant participant, String id)
+ {
+ this.participant = participant;
+ this.id = id;
+ }
+
+ // empty constructor for crash recovery
+
+ public LocalParticipantCompletionParticipantStub()
+ {
+ this.participant = null;
+ this.id = null;
+ }
+
+ /**
+ * establish a back channel from the coordinator side protocol engine to the coordinator.
+ *
+ * @param participantManager a manager which will forward incoming remote participant requests to the coordinator
+ */
+ public void setParticipantManager(BAParticipantManager participantManager) {
+ // currently unimplemented this really needs help from the applicaton helper
+ }
+
+ /**
+ * The transaction has completed successfully. The participant previously
+ * informed the coordinator that it was ready to complete.
+ */
+
+ public void close() throws com.arjuna.wst.WrongStateException, com.arjuna.wst.SystemException {
+ participant.close();
+ }
+
+ /**
+ * The transaction has cancelled, and the participant should undo any work.
+ * The participant cannot have informed the coordinator that it has
+ * completed.
+ */
+
+ public void cancel() throws com.arjuna.wst.FaultedException, com.arjuna.wst.WrongStateException, com.arjuna.wst.SystemException {
+ participant.cancel();
+ }
+
+ /**
+ * The transaction has cancelled. The participant previously
+ * informed the coordinator that it had finished work but could compensate
+ * later if required, so it is now requested to do so.
+ *
+ * @throws com.arjuna.wst.FaultedException
+ * if the participant was unable to
+ * perform the required compensation action because of an
+ * unrecoverable error. The coordinator is notified of this fault
+ * and as a result will stop resending compensation requests.
+ * @throws com.arjuna.wst.SystemException if the participant was unable to
+ * perform the required compensation action because of a transient
+ * fault. The coordinator is not notified of this fault so it
+ * will retry the compensate request after a suitable timeout.
+ */
+
+ public void compensate() throws FaultedException, com.arjuna.wst.WrongStateException, com.arjuna.wst.SystemException {
+ participant.compensate();
+ }
+
+ /**
+ * @return the status value.
+ */
+
+ public String status() throws com.arjuna.wst.SystemException {
+ return participant.status();
+ }
+
+ /**
+ * If the participant enquires as to the status of the transaction it was
+ * registered with and that transaction is no longer available (has rolled
+ * back) then this operation will be invoked by the coordination service.
+ */
+
+ public void unknown() throws com.arjuna.wst.SystemException {
+ participant.unknown();
+ }
+
+ /**
+ * If the participant enquired as to the status of the transaction it was
+ * registered with and an error occurs (e.g., the transaction service is
+ * unavailable) then this operation will be invoked.
+ */
+
+ public void error() throws com.arjuna.wst.SystemException {
+ participant.error();
+ }
+
+ /**
+ * Save the state of the particpant to the specified input object stream.
+ *
+ * @param oos The output output stream.
+ * @return true if persisted, false otherwise.
+ */
+ public boolean saveState(OutputObjectState oos) {
+ // this needs to check if patrticipant is either serializable or a PersistableBAParticipant and
+ // convert it to a byte[] as appropriate then save the byte[] array to the stream
+
+ // save id
+ // identify and save recreate mode (serialization or recreation)
+ // retrieve byte[]
+ // save byte[] valid flag
+ // if valid flag save byte[]
+
+ return false;
+ }
+
+ /**
+ * Restore the state of the particpant from the specified input object stream.
+ *
+ * @param ios The Input object stream.
+ * @return true if restored, false otherwise.
+ */
+ public boolean restoreState(InputObjectState ios) {
+ // this needs to retrieve a byte[] from the stream and then locate a regsitered helper to
+ // either deserialize the participant or recreate a new one.
+
+ // restore id
+ // restore recreate mode (serialization or recreation)
+ // restore byte[] valid flag
+ // if valid flag restore byte[]
+ // iterate over helpers invoking ParticipantCompletion recreate or deserialize helper until
+ // one of them returns a participant
+ // return true if participant found otherwise false
+
+ return false;
+ }
+
+ protected BusinessAgreementWithParticipantCompletionParticipant participant;
+ protected String id;
+
+}
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src10/org/jboss/jbossts/xts10/recovery/participant/ba/BAParticipantRecoveryRecord.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/org/jboss/jbossts/xts10/recovery/participant/ba/BAParticipantRecoveryRecord.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/org/jboss/jbossts/xts10/recovery/participant/ba/BAParticipantRecoveryRecord.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,109 @@
+package org.jboss.jbossts.xts10.recovery.participant.ba;
+
+import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
+import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
+import com.arjuna.wst.messaging.engines.ParticipantCompletionParticipantEngine;
+import com.arjuna.wst.messaging.engines.CoordinatorCompletionParticipantEngine;
+import com.arjuna.ats.arjuna.state.OutputObjectState;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+import com.arjuna.webservices.wsaddr.EndpointReferenceType;
+import com.arjuna.webservices.wsba.State;
+import com.arjuna.webservices.util.StreamHelper;
+import com.arjuna.webservices.wsba.processors.ParticipantCompletionParticipantProcessor;
+import com.arjuna.webservices.wsba.processors.CoordinatorCompletionParticipantProcessor;
+
+import javax.xml.stream.*;
+import javax.xml.namespace.QName;
+import java.io.StringWriter;
+import java.io.IOException;
+import java.io.StringReader;
+
+/**
+ * recovery record specific to WS-BA 1.0 protocol participants. this implements the behaviours
+ * necessary to save and restore a 1.0 participant to or from the TX object store
+ */
+public class BAParticipantRecoveryRecord extends org.jboss.jbossts.xts.recovery.participant.ba.BAParticipantRecoveryRecord {
+
+ /**
+ * constructor used during prepare processing to create an AT 1.0 participant record
+ * for saving to the object store
+ * @param id the id of the application-specific participant
+ * @param participant the application-specific participant
+ * @param
+ */
+ public BAParticipantRecoveryRecord(String id, BusinessAgreementWithParticipantCompletionParticipant participant, boolean isParticipantCompletion, EndpointReferenceType endpoint)
+ {
+ super(id, participant, isParticipantCompletion);
+ this.endpoint = endpoint;
+ }
+
+ /**
+ * constructor used during recovery processing to create a record whose contents will be
+ * recovered from the object store
+ */
+ public BAParticipantRecoveryRecord()
+ {
+ super(null, null, false);
+ }
+
+ /**
+ * save the endpoint reference to the coordinator for this participant
+ */
+ protected void saveEndpointReference(OutputObjectState oos) throws IOException, XMLStreamException{
+ // save an XML representation of the endpoint
+ XMLOutputFactory factory = XMLOutputFactory.newInstance();
+ StringWriter stringWriter = new StringWriter();
+ XMLStreamWriter writer = factory.createXMLStreamWriter(stringWriter);
+ StreamHelper.writeStartElement(writer, QNAME_SAGAS_COORDINATOR) ;
+ endpoint.writeContent(writer);
+ StreamHelper.writeEndElement(writer, null, null) ;
+ writer.close();
+ oos.packString(stringWriter.toString());
+ }
+
+ /**
+ * restore the endpoint reference to the coordinator for this participant
+ */
+ protected void restoreEndpointReference(InputObjectState ios) throws IOException, XMLStreamException{
+ // restore and parse the XML representation of the endpoint
+ String xmlEndpoint = ios.unpackString();
+ StringReader stringReader = new StringReader(xmlEndpoint);
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+ XMLStreamReader reader = factory.createXMLStreamReader(stringReader);
+ StreamHelper.checkNextStartTag(reader, QNAME_SAGAS_COORDINATOR) ;
+ endpoint = new EndpointReferenceType(reader);
+ }
+
+ /**
+ * create a participant engine to manage commit or rollback processing for the
+ * participant and install it in the active participants table
+ */
+ public void activate() {
+ if (isParticipantCompletion) {
+ ParticipantCompletionParticipantEngine engine = new ParticipantCompletionParticipantEngine(id, endpoint, participant, State.STATE_COMPLETED, true);
+ ParticipantCompletionParticipantProcessor.getProcessor().activateParticipant(engine, getId());
+ } else {
+ BusinessAgreementWithCoordinatorCompletionParticipant coordinatorCompletionParticipant = (BusinessAgreementWithCoordinatorCompletionParticipant) participant;
+ CoordinatorCompletionParticipantEngine engine = new CoordinatorCompletionParticipantEngine(id, endpoint, coordinatorCompletionParticipant, State.STATE_COMPLETED, true);
+ CoordinatorCompletionParticipantProcessor.getProcessor().activateParticipant(engine, getId());
+ }
+ }
+
+ /**
+ * test whether a participant is currently activated with the id of this recovery record.
+ *
+ * @return true if a participant is currently activated with the id of this recovery record
+ */
+ public boolean isActive()
+ {
+ if (isParticipantCompletion) {
+ return ParticipantCompletionParticipantProcessor.getProcessor().isActive(getId());
+ } else {
+ return CoordinatorCompletionParticipantProcessor.getProcessor().isActive(getId());
+ }
+ }
+
+ private static final QName QNAME_SAGAS_COORDINATOR = new QName("sagasCoordinator") ;
+
+ private EndpointReferenceType endpoint;
+}
\ No newline at end of file
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/webservices11/wsba/processors/CoordinatorCompletionParticipantProcessor.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/webservices11/wsba/processors/CoordinatorCompletionParticipantProcessor.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/webservices11/wsba/processors/CoordinatorCompletionParticipantProcessor.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -75,6 +75,12 @@
public abstract void deactivateParticipant(final CoordinatorCompletionParticipantInboundEvents participant) ;
/**
+ * Check whether a participant with the given id is currently active
+ * @param identifier The identifier.
+ */
+ public abstract boolean isActive(final String identifier) ;
+
+ /**
* Cancel.
* @param cancel The cancel notification.
* @param addressingProperties The addressing context.
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/webservices11/wsba/processors/ParticipantCompletionParticipantProcessor.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/webservices11/wsba/processors/ParticipantCompletionParticipantProcessor.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/webservices11/wsba/processors/ParticipantCompletionParticipantProcessor.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -74,6 +74,12 @@
public abstract void deactivateParticipant(final ParticipantCompletionParticipantInboundEvents participant) ;
/**
+ * Check whether a participant with the given id is currently active
+ * @param identifier The identifier.
+ */
+ public abstract boolean isActive(final String identifier) ;
+
+ /**
* Cancel.
* @param cancel The cancel notification.
* @param addressingProperties The addressing context.
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/RecoverableBusinessAgreementWithCoordinatorCompletionParticipant.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/RecoverableBusinessAgreementWithCoordinatorCompletionParticipant.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/RecoverableBusinessAgreementWithCoordinatorCompletionParticipant.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,45 @@
+/*
+ * 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, 2003, 2004,
+ *
+ * Arjuna Technologies Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: BusinessAgreementWithCoordinatorCompletionParticipant.java,v 1.1.2.1 2004/05/26 10:02:33 nmcl Exp $
+ */
+
+package com.arjuna.wst11;
+
+import com.arjuna.wst.WrongStateException;
+import com.arjuna.wst.SystemException;
+import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
+
+/**
+ * extension of BusinessAgreementWithCoordinatorCompletionParticipant API which allows establishment of a
+ * back channel from the coordinator side protocol engine to the coordinator.
+ */
+
+public interface RecoverableBusinessAgreementWithCoordinatorCompletionParticipant extends RecoverableBusinessAgreementWithParticipantCompletionParticipant, BusinessAgreementWithCoordinatorCompletionParticipant
+{
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/RecoverableBusinessAgreementWithParticipantCompletionParticipant.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/RecoverableBusinessAgreementWithParticipantCompletionParticipant.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/RecoverableBusinessAgreementWithParticipantCompletionParticipant.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,51 @@
+/*
+ * 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, 2003, 2004,
+ *
+ * Arjuna Technologies Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: BusinessAgreementWithParticipantCompletionParticipant.java,v 1.1.2.1 2004/05/26 10:02:33 nmcl Exp $
+ */
+
+package com.arjuna.wst11;
+
+import com.arjuna.wst.WrongStateException;
+import com.arjuna.wst.SystemException;
+import com.arjuna.wst.FaultedException;
+import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
+
+/**
+ * extension of BusinessAgreementWithParticipantCompletionParticipant API which allows establishment of a
+ * back channel from the coordinator side protocol engine to the coordinator.
+ */
+public interface RecoverableBusinessAgreementWithParticipantCompletionParticipant extends BusinessAgreementWithParticipantCompletionParticipant
+{
+ /**
+ * establish a back channel from the coordinator side protocol engine to the coordinator.
+ *
+ * @param participantManager a manager which will forward incoming remote participant requests to the coordinator
+ */
+ public void setParticipantManager(BAParticipantManager participantManager);
+}
\ No newline at end of file
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/CoordinatorCompletionCoordinatorProcessorImpl.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/CoordinatorCompletionCoordinatorProcessorImpl.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/CoordinatorCompletionCoordinatorProcessorImpl.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -35,6 +35,7 @@
import org.oasis_open.docs.ws_tx.wsba._2006._06.ExceptionType;
import org.oasis_open.docs.ws_tx.wsba._2006._06.NotificationType;
import org.oasis_open.docs.ws_tx.wsba._2006._06.StatusType;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
import javax.xml.ws.addressing.AddressingProperties;
@@ -86,8 +87,8 @@
* @param addressingProperties The addressing context.
* @param arjunaContext The arjuna context.
*
- * @message com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.canceled_1 [com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.cancelled_1] - Unexpected exception thrown from cancelled:
- * @message com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.canceled_2 [com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.cancelled_2] - Cancelled called on unknown coordinator: {0}
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.cancelled_1 [com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.canceled_1] - Unexpected exception thrown from cancelled:
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.cancelled_2 [com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.canceled_2] - Cancelled called on unknown coordinator: {0}
*/
public void cancelled(final NotificationType cancelled, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
@@ -104,13 +105,13 @@
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.canceled_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.cancelled_1", th) ;
}
}
}
else if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.canceled_2", new Object[] {instanceIdentifier}) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.cancelled_2", new Object[] {instanceIdentifier}) ;
}
}
@@ -190,7 +191,8 @@
*
* @message com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fail_1 [com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fail_1] - Unexpected exception thrown from failed:
* @message com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fail_2 [com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fail_2] - Failed called on unknown coordinator: {0}
- */
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fail_3 [com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fail_3] - Ignoring fail called on unidentified coordinator until recovery pass is complete: {0}
+ */
public void fail(final ExceptionType fail, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
final InstanceIdentifier instanceIdentifier = arjunaContext.getInstanceIdentifier() ;
@@ -209,11 +211,19 @@
WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fail_1", th) ;
}
}
- }
- else if (WSTLogger.arjLoggerI18N.isDebugEnabled())
- {
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fail_2", new Object[] {instanceIdentifier}) ;
+ } else if (areRecoveryLogEntriesAccountedFor()) {
+ // we can respond with a failed as the participant is not pending recovery
+ if (WSTLogger.arjLoggerI18N.isDebugEnabled())
+ {
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fail_2", new Object[] {instanceIdentifier}) ;
+ }
sendFailed(addressingProperties, arjunaContext) ;
+ } else {
+ // we must delay responding until we can be sure there is no participant pending recovery
+ if (WSTLogger.arjLoggerI18N.isDebugEnabled())
+ {
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.fail_3", new Object[] {instanceIdentifier}) ;
+ }
}
}
@@ -225,6 +235,7 @@
*
* @message com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_1 [com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_1] - Unexpected exception thrown from completed:
* @message com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_2 [com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_2] - Completed called on unknown coordinator: {0}
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_3 [com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_3] - Ignoring completed called on unidentified coordinator until recovery pass is complete: {0}
*/
public void completed(final NotificationType completed, final AddressingProperties addressingProperties,
final ArjunaContext arjunaContext)
@@ -248,7 +259,13 @@
}
else if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_2", new Object[] {instanceIdentifier}) ;
+ if (areRecoveryLogEntriesAccountedFor()) {
+ // this is a resend for a lost participant
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_2", new Object[] {instanceIdentifier}) ;
+ } else {
+ // this may be a resend for a participant still pending recovery
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.CoordinatorCompletionCoordinatorProcessorImpl.completed_3", new Object[] {instanceIdentifier}) ;
+ }
}
}
@@ -515,4 +532,15 @@
}
}
}
+
+ /**
+ * Tests if there may be unknown coordinator entries in the recovery log.
+ *
+ * @return false if there may be unknown coordinator entries in the recovery log.
+ */
+
+ private static boolean areRecoveryLogEntriesAccountedFor()
+ {
+ return XTSBARecoveryManager.getRecoveryManager().isCoordinatorRecoveryStarted();
+ }
}
\ No newline at end of file
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/CoordinatorCompletionParticipantProcessorImpl.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/CoordinatorCompletionParticipantProcessorImpl.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/CoordinatorCompletionParticipantProcessorImpl.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -35,6 +35,7 @@
import com.arjuna.wsc11.messaging.MessageId;
import org.oasis_open.docs.ws_tx.wsba._2006._06.NotificationType;
import org.oasis_open.docs.ws_tx.wsba._2006._06.StatusType;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
import javax.xml.ws.addressing.AddressingProperties;
import javax.xml.namespace.QName;
@@ -71,6 +72,15 @@
}
/**
+ * Check whether a participant with the given id is currently active
+ *
+ * @param identifier The identifier.
+ */
+ public boolean isActive(String identifier) {
+ return activatedObjectProcessor.getObject(identifier) != null;
+ }
+
+ /**
* Get the participant with the specified identifier.
* @param instanceIdentifier The participant identifier.
* @return The participant or null if not known.
@@ -89,10 +99,30 @@
*
* @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_1 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_1] - Unexpected exception thrown from cancel:
* @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_2 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_2] - Cancel called on unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_3 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_3] - Cancel request dropped pending WS-BA participant recovery manager initialization for participant: {0}
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_4 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_4] - Cancel request dropped pending WS-BA participant recovery manager scan for unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_5 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_5] - Cancel request dropped pending registration of application-specific recovery module for WS-BA participant: {0}
*/
public void cancel(final NotificationType cancel, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
final InstanceIdentifier instanceIdentifier = arjunaContext.getInstanceIdentifier() ;
+
+ /**
+ * ensure the BA participant recovery manager is running
+ */
+
+ XTSBARecoveryManager recoveryManager = XTSBARecoveryManager.getRecoveryManager();
+
+ if (recoveryManager == null) {
+ // log warning and drop this message -- it will be resent
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_3", new Object[] {instanceIdentifier}) ;
+ }
+
+ return;
+ }
+
final CoordinatorCompletionParticipantInboundEvents participant = getParticipant(instanceIdentifier) ;
if (participant != null)
@@ -109,6 +139,20 @@
}
}
}
+ else if (!recoveryManager.isParticipantRecoveryStarted())
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_4", new Object[] {instanceIdentifier}) ;
+ }
+ }
+ else if (recoveryManager.findParticipantRecoveryRecord(instanceIdentifier.getInstanceIdentifier()) != null)
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.cancel_5", new Object[] {instanceIdentifier}) ;
+ }
+ }
else
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
@@ -127,10 +171,30 @@
*
* @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_1 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_1] - Unexpected exception thrown from close:
* @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_2 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_2] - Close called on unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_3 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_3] - Close request dropped pending WS-BA participant recovery manager initialization for participant: {0}
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_4 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_4] - Close request dropped pending WS-BA participant recovery manager scan for unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_5 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_5] - Close request dropped pending registration of application-specific recovery module for WS-BA participant: {0}
*/
public void close(final NotificationType close, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
final InstanceIdentifier instanceIdentifier = arjunaContext.getInstanceIdentifier() ;
+
+ /**
+ * ensure the BA participant recovery manager is running
+ */
+
+ XTSBARecoveryManager recoveryManager = XTSBARecoveryManager.getRecoveryManager();
+
+ if (recoveryManager == null) {
+ // log warning and drop this message -- it will be resent
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_3", new Object[] {instanceIdentifier}) ;
+ }
+
+ return;
+ }
+
final CoordinatorCompletionParticipantInboundEvents participant = getParticipant(instanceIdentifier) ;
if (participant != null)
@@ -147,6 +211,20 @@
}
}
}
+ else if (!recoveryManager.isParticipantRecoveryStarted())
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_4", new Object[] {instanceIdentifier}) ;
+ }
+ }
+ else if (recoveryManager.findParticipantRecoveryRecord(instanceIdentifier.getInstanceIdentifier()) != null)
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.close_5", new Object[] {instanceIdentifier}) ;
+ }
+ }
else
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
@@ -165,10 +243,30 @@
*
* @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_1 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_1] - Unexpected exception thrown from compensate:
* @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_2 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_2] - Compensate called on unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_3 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_3] - Compensate request dropped pending WS-BA participant recovery manager initialization for participant: {0}
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_4 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_4] - Compensate request dropped pending WS-BA participant recovery manager scan for unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_5 [com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_5] - Compensate request dropped pending registration of application-specific recovery module for WS-BA participant: {0}
*/
public void compensate(final NotificationType compensate, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
final InstanceIdentifier instanceIdentifier = arjunaContext.getInstanceIdentifier() ;
+
+ /**
+ * ensure the BA participant recovery manager is running
+ */
+
+ XTSBARecoveryManager recoveryManager = XTSBARecoveryManager.getRecoveryManager();
+
+ if (recoveryManager == null) {
+ // log warning and drop this message -- it will be resent
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_3", new Object[] {instanceIdentifier}) ;
+ }
+
+ return;
+ }
+
final CoordinatorCompletionParticipantInboundEvents participant = getParticipant(instanceIdentifier) ;
if (participant != null)
@@ -185,6 +283,20 @@
}
}
}
+ else if (!recoveryManager.isParticipantRecoveryStarted())
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_4", new Object[] {instanceIdentifier}) ;
+ }
+ }
+ else if (recoveryManager.findParticipantRecoveryRecord(instanceIdentifier.getInstanceIdentifier()) != null)
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.CoordinatorCompletionParticipantProcessorImpl.compensate_5", new Object[] {instanceIdentifier}) ;
+ }
+ }
else
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/ParticipantCompletionCoordinatorProcessorImpl.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/ParticipantCompletionCoordinatorProcessorImpl.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/ParticipantCompletionCoordinatorProcessorImpl.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -27,14 +27,13 @@
import com.arjuna.webservices11.wsarj.ArjunaContext;
import com.arjuna.webservices11.wsarj.InstanceIdentifier;
import com.arjuna.webservices11.wsba.ParticipantCompletionCoordinatorInboundEvents;
-import com.arjuna.webservices11.wsba.BusinessActivityConstants;
import com.arjuna.webservices11.wsba.client.ParticipantCompletionParticipantClient;
import com.arjuna.webservices11.wsba.processors.ParticipantCompletionCoordinatorProcessor;
-import com.arjuna.webservices11.ServiceRegistry;
import com.arjuna.wsc11.messaging.MessageId;
import org.oasis_open.docs.ws_tx.wsba._2006._06.ExceptionType;
import org.oasis_open.docs.ws_tx.wsba._2006._06.NotificationType;
import org.oasis_open.docs.ws_tx.wsba._2006._06.StatusType;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
import javax.xml.ws.addressing.AddressingProperties;
@@ -190,6 +189,7 @@
*
* @message com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_1 [com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_1] - Unexpected exception thrown from completed:
* @message com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_2 [com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_2] - Completed called on unknown coordinator: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_3 [com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_3] - Ignoring completed called on unidentified coordinator until recovery pass is complete: {0}
*/
public void completed(final NotificationType completed, final AddressingProperties addressingProperties,
final ArjunaContext arjunaContext)
@@ -213,7 +213,13 @@
}
else if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_2", new Object[] {instanceIdentifier}) ;
+ if (areRecoveryLogEntriesAccountedFor()) {
+ // this is a resend for a lost participant
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_2", new Object[] {instanceIdentifier}) ;
+ } else {
+ // this may be a resend for a participant still pending recovery
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_3", new Object[] {instanceIdentifier}) ;
+ }
}
}
@@ -245,10 +251,12 @@
WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_1", th) ;
}
}
- }
- else if (WSTLogger.arjLoggerI18N.isDebugEnabled())
- {
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_2", new Object[] {instanceIdentifier}) ;
+ } else {
+ if (WSTLogger.arjLoggerI18N.isDebugEnabled())
+ {
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.completed_2", new Object[] {instanceIdentifier}) ;
+ }
+
sendNotCompleted(addressingProperties, arjunaContext) ;
}
}
@@ -280,9 +288,7 @@
WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.exit_1", th) ;
}
}
- }
- else
- {
+ } else {
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.exit_2", new Object[] {instanceIdentifier}) ;
@@ -292,13 +298,14 @@
}
/**
- * Fault.
- * @param fail The fault notification.
+ * Fail.
+ * @param fail The fail notification.
* @param addressingProperties The addressing context.
* @param arjunaContext The arjuna context.
*
- * @message com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_1 [com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_1] - Unexpected exception thrown from fault:
- * @message com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_2 [com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_2] - Fault called on unknown coordinator: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fail_1 [com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fail_1] - Unexpected exception thrown from fail:
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fail_2 [com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fail_2] - Fail called on unknown coordinator: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fail_3 [com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fail_3] - Ignoring fail called on unidentified coordinator until recovery pass is complete: {0}
*/
public void fail(final ExceptionType fail, final AddressingProperties addressingProperties,
final ArjunaContext arjunaContext)
@@ -316,17 +323,22 @@
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fail_1", th) ;
}
}
- }
- else
- {
+ } else if (areRecoveryLogEntriesAccountedFor()) {
+ // we can respond with a failed as the participant is not pending recovery
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fault_2", new Object[] {instanceIdentifier}) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fail_2", new Object[] {instanceIdentifier}) ;
}
sendFailed(addressingProperties, arjunaContext) ;
+ } else {
+ // we must delay responding until we can be sure there is no participant pending recovery
+ if (WSTLogger.arjLoggerI18N.isDebugEnabled())
+ {
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.fail_3", new Object[] {instanceIdentifier}) ;
+ }
}
}
@@ -360,7 +372,7 @@
}
else if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.getStatus_2", new Object[] {instanceIdentifier}) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.getStatus_2", new Object[] {instanceIdentifier}) ;
}
}
@@ -461,12 +473,12 @@
}
/**
- * Send a faulted message.
+ * Send a failed message.
*
* @param addressingProperties The addressing context.
* @param arjunaContext The arjuna context.
*
- * @message com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.sendFaulted_1 [com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.sendFaulted_1] - Unexpected exception while sending Faulted
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.sendFailed_1 [com.arjuna.wst11.messaging.ParticipantCompletionCoordinatorProcessorImpl.sendFailed_1] - Unexpected exception while sending Failed
*/
private void sendFailed(final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
@@ -513,4 +525,15 @@
}
}
}
+
+ /**
+ * Tests if there may be unknown coordinator entries in the recovery log.
+ *
+ * @return false if there may be unknown coordinator entries in the recovery log.
+ */
+
+ private static boolean areRecoveryLogEntriesAccountedFor()
+ {
+ return XTSBARecoveryManager.getRecoveryManager().isCoordinatorRecoveryStarted();
+ }
}
\ No newline at end of file
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/ParticipantCompletionParticipantProcessorImpl.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/ParticipantCompletionParticipantProcessorImpl.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/ParticipantCompletionParticipantProcessorImpl.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -34,6 +34,7 @@
import com.arjuna.wsc11.messaging.MessageId;
import org.oasis_open.docs.ws_tx.wsba._2006._06.NotificationType;
import org.oasis_open.docs.ws_tx.wsba._2006._06.StatusType;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
import javax.xml.ws.addressing.AddressingProperties;
@@ -69,6 +70,15 @@
}
/**
+ * Check whether a participant with the given id is currently active
+ * @param identifier The identifier.
+ */
+ public boolean isActive(final String identifier)
+ {
+ return activatedObjectProcessor.getObject(identifier) != null;
+ }
+
+ /**
* Get the participant with the specified identifier.
* @param instanceIdentifier The participant identifier.
* @return The participant or null if not known.
@@ -87,10 +97,30 @@
*
* @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_1 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_1] - Unexpected exception thrown from cancel:
* @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_2 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_2] - Cancel called on unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_3 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_3] - Cancel request dropped pending WS-BA participant recovery manager initialization for participant: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_4 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_4] - Cancel request dropped pending WS-BA participant recovery manager scan for unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_5 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_5] - Cancel request dropped pending registration of application-specific recovery module for WS-BA participant: {0}
*/
public void cancel(final NotificationType cancel, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
final InstanceIdentifier instanceIdentifier = arjunaContext.getInstanceIdentifier() ;
+
+ /**
+ * ensure the BA participant recovery manager is running
+ */
+
+ XTSBARecoveryManager recoveryManager = XTSBARecoveryManager.getRecoveryManager();
+
+ if (recoveryManager == null) {
+ // log warning and drop this message -- it will be resent
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_3", new Object[] {instanceIdentifier}) ;
+ }
+
+ return;
+ }
+
final ParticipantCompletionParticipantInboundEvents participant = getParticipant(instanceIdentifier) ;
if (participant != null)
@@ -107,6 +137,20 @@
}
}
}
+ else if (!recoveryManager.isParticipantRecoveryStarted())
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_4", new Object[] {instanceIdentifier}) ;
+ }
+ }
+ else if (recoveryManager.findParticipantRecoveryRecord(instanceIdentifier.getInstanceIdentifier()) != null)
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.cancel_5", new Object[] {instanceIdentifier}) ;
+ }
+ }
else
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
@@ -125,10 +169,30 @@
*
* @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_1 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_1] - Unexpected exception thrown from close:
* @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_2 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_2] - Close called on unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_3 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_3] - Close request dropped pending WS-BA participant recovery manager initialization for participant: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_4 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_4] - Close request dropped pending WS-BA participant recovery manager scan for unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_5 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_5] - Close request dropped pending registration of application-specific recovery module for WS-BA participant: {0}
*/
public void close(final NotificationType close, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
final InstanceIdentifier instanceIdentifier = arjunaContext.getInstanceIdentifier() ;
+
+ /**
+ * ensure the BA participant recovery manager is running
+ */
+
+ XTSBARecoveryManager recoveryManager = XTSBARecoveryManager.getRecoveryManager();
+
+ if (recoveryManager == null) {
+ // log warning and drop this message -- it will be resent
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_3", new Object[] {instanceIdentifier}) ;
+ }
+
+ return;
+ }
+
final ParticipantCompletionParticipantInboundEvents participant = getParticipant(instanceIdentifier) ;
if (participant != null)
@@ -145,6 +209,20 @@
}
}
}
+ else if (!recoveryManager.isParticipantRecoveryStarted())
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_4", new Object[] {instanceIdentifier}) ;
+ }
+ }
+ else if (recoveryManager.findParticipantRecoveryRecord(instanceIdentifier.getInstanceIdentifier()) != null)
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.close_5", new Object[] {instanceIdentifier}) ;
+ }
+ }
else
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
@@ -163,10 +241,30 @@
*
* @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_1 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_1] - Unexpected exception thrown from compensate:
* @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_2 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_2] - Compensate called on unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_3 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_3] - Compensate request dropped pending WS-BA participant recovery manager initialization for participant: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_4 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_4] - Compensate request dropped pending WS-BA participant recovery manager scan for unknown participant: {0}
+ * @message com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_5 [com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_5] - Compensate request dropped pending registration of application-specific recovery module for WS-BA participant: {0}
*/
public void compensate(final NotificationType compensate, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
final InstanceIdentifier instanceIdentifier = arjunaContext.getInstanceIdentifier() ;
+
+ /**
+ * ensure the BA participant recovery manager is running
+ */
+
+ XTSBARecoveryManager recoveryManager = XTSBARecoveryManager.getRecoveryManager();
+
+ if (recoveryManager == null) {
+ // log warning and drop this message -- it will be resent
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_3", new Object[] {instanceIdentifier}) ;
+ }
+
+ return;
+ }
+
final ParticipantCompletionParticipantInboundEvents participant = getParticipant(instanceIdentifier) ;
if (participant != null)
@@ -183,6 +281,20 @@
}
}
}
+ else if (!recoveryManager.isParticipantRecoveryStarted())
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_4", new Object[] {instanceIdentifier}) ;
+ }
+ }
+ else if (recoveryManager.findParticipantRecoveryRecord(instanceIdentifier.getInstanceIdentifier()) != null)
+ {
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.ParticipantCompletionParticipantProcessorImpl.compensate_5", new Object[] {instanceIdentifier}) ;
+ }
+ }
else
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/CoordinatorCompletionCoordinatorEngine.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/CoordinatorCompletionCoordinatorEngine.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/CoordinatorCompletionCoordinatorEngine.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -75,6 +75,10 @@
* The current state.
*/
private State state;
+ /**
+ * The flag indicating that this coordinator has been recovered from the log.
+ */
+ private boolean recovered ;
/**
* Construct the initial engine for the coordinator.
@@ -83,7 +87,7 @@
*/
public CoordinatorCompletionCoordinatorEngine(final String id, final W3CEndpointReference participant)
{
- this(id, participant, State.STATE_ACTIVE) ;
+ this(id, participant, State.STATE_ACTIVE, false) ;
}
/**
@@ -93,12 +97,13 @@
* @param state The initial state.
*/
public CoordinatorCompletionCoordinatorEngine(final String id, final W3CEndpointReference participant,
- final State state)
+ final State state, final boolean recovered)
{
this.id = id ;
this.instanceIdentifier = new InstanceIdentifier(id) ;
this.participant = participant ;
this.state = state ;
+ this.recovered = recovered;
}
/**
@@ -108,7 +113,13 @@
public void setCoordinator(final BAParticipantManager coordinator)
{
this.coordinator = coordinator ;
- CoordinatorCompletionCoordinatorProcessor.getProcessor().activateCoordinator(this, id) ;
+ // unrecovered participants are always activated
+ // we only need to reactivate recovered participants which were successfully COMPLETED or which began
+ // CLOSING. any others will only have been saved because of a heuristic outcome. we can safely drop
+ // them since we implement presumed abort.
+ if (!recovered || state == State.STATE_COMPLETED || state == State.STATE_CLOSING) {
+ CoordinatorCompletionCoordinatorProcessor.getProcessor().activateCoordinator(this, id) ;
+ }
}
/**
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/CoordinatorCompletionParticipantEngine.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/CoordinatorCompletionParticipantEngine.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/CoordinatorCompletionParticipantEngine.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -23,7 +23,6 @@
import com.arjuna.webservices.SoapFault;
import com.arjuna.webservices.logging.WSTLogger;
import com.arjuna.webservices.util.TransportTimer;
-import com.arjuna.webservices.wsarjtx.ArjunaTXConstants;
import com.arjuna.webservices11.wsaddr.AddressingHelper;
import com.arjuna.webservices11.wsarj.ArjunaContext;
import com.arjuna.webservices11.wsarj.InstanceIdentifier;
@@ -32,12 +31,13 @@
import com.arjuna.webservices11.wsba.BusinessActivityConstants;
import com.arjuna.webservices11.wsba.client.CoordinatorCompletionCoordinatorClient;
import com.arjuna.webservices11.wsba.processors.CoordinatorCompletionParticipantProcessor;
-import com.arjuna.webservices11.ServiceRegistry;
import com.arjuna.wsc11.messaging.MessageId;
import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
import com.arjuna.wst.FaultedException;
import org.oasis_open.docs.ws_tx.wsba._2006._06.NotificationType;
import org.oasis_open.docs.ws_tx.wsba._2006._06.StatusType;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
+import org.jboss.jbossts.xts11.recovery.participant.ba.BAParticipantRecoveryRecord;
import javax.xml.namespace.QName;
import javax.xml.ws.addressing.AddressingProperties;
@@ -74,8 +74,18 @@
* The associated timer task or null.
*/
private TimerTask timerTask ;
+ /**
+ * true if this participant has been recovered otherwise false
+ */
+ private boolean recovered;
/**
+ * true if this participant's recovery details have been logged to disk otherwise false
+ */
+ private boolean persisted;
+
+
+ /**
* Construct the initial engine for the participant.
* @param id The participant id.
* @param coordinator The coordinator endpoint reference.
@@ -84,7 +94,7 @@
public CoordinatorCompletionParticipantEngine(final String id, final W3CEndpointReference coordinator,
final BusinessAgreementWithCoordinatorCompletionParticipant participant)
{
- this(id, coordinator, participant, State.STATE_ACTIVE) ;
+ this(id, coordinator, participant, State.STATE_ACTIVE, false) ;
}
/**
@@ -93,15 +103,18 @@
* @param coordinator The coordinator endpoint reference.
* @param participant The participant.
* @param state The initial state.
+ * @param recovered true if the engine has been recovered from th elog otherwise false
*/
public CoordinatorCompletionParticipantEngine(final String id, final W3CEndpointReference coordinator,
- final BusinessAgreementWithCoordinatorCompletionParticipant participant, final State state)
+ final BusinessAgreementWithCoordinatorCompletionParticipant participant, final State state, final boolean recovered)
{
this.id = id ;
this.instanceIdentifier = new InstanceIdentifier(id) ;
this.coordinator = coordinator ;
this.participant = participant ;
this.state = state ;
+ this.recovered = recovered;
+ this.persisted = recovered;
}
/**
@@ -136,10 +149,14 @@
}
}
- if ((current == State.STATE_ACTIVE) || (current == State.STATE_COMPLETING))
+ if (current == State.STATE_ACTIVE)
{
- executeCancel() ;
+ executeCancel(false) ;
}
+ else if (current == State.STATE_COMPLETING)
+ {
+ executeCancel(true) ;
+ }
else if (current == State.STATE_COMPLETED)
{
sendCompleted() ;
@@ -366,19 +383,33 @@
* NotCompleting -> NotCompleting (invalid state)
* Exiting -> Exiting (invalid state)
* Ended -> Ended
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.failed_1 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.failed_1] - Unable to delete recovery record during failed for WS-BA participant {0}
*/
public void failed(final NotificationType failed, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
final State current ;
+ boolean deleteRequired = false;
synchronized(this)
{
current = state ;
if ((current == State.STATE_FAILING_ACTIVE) || (current == State.STATE_FAILING_CANCELING) ||
(current == State.STATE_FAILING_COMPLETING) || (current == State.STATE_FAILING_COMPENSATING))
{
+ deleteRequired = persisted;
ended() ;
}
}
+ // if we just ended the participant ensure any log record gets deleted
+
+ if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- nothing more we can do than log a message
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.failed_1", new Object[] {id}) ;
+ }
+ }
+ }
}
/**
@@ -448,15 +479,31 @@
* @param soapFault The soap fault.
* @param addressingProperties The addressing context.
* @param arjunaContext The arjuna context.
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.soapFault_1 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.soapFault_1] - Unable to delete recovery record during soapFault processing for WS-BA participant {0}
*/
public void soapFault(final SoapFault soapFault, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
- ended() ;
- try
- {
- participant.error() ;
- }
- catch (final Throwable th) {} // ignore
+ boolean deleteRequired;
+ synchronized(this) {
+ deleteRequired = persisted;
+ ended() ;
+ }
+ // TODO -- clarify when and why this gets called and update doc in interface. also check unknown()
+ try
+ {
+ participant.error() ;
+ }
+ catch (final Throwable th) {} // ignore
+ // if we just ended the participant ensure any log record gets deleted
+ if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- nothing more we can do than log a message
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.soapFault_1", new Object[] {id}) ;
+ }
+ }
+ }
}
/**
@@ -475,10 +522,14 @@
* Exiting -> Exiting (invalid state)
* NotCompleting -> NotCompleting (invalid state)
* Ended -> Ended (invalid state)
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.completed_1 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.completed_1] - Unable to write recovery record during completed for WS-BA participant {0}
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.completed_2 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.completed_2] - Unable to delete recovery record during completed for WS-BA participant {0}
*/
public State completed()
{
- final State current ;
+ State current ;
+ boolean failRequired = false;
+ boolean deleteRequired = false;
synchronized(this)
{
current = state ;
@@ -488,8 +539,55 @@
}
}
- if ((current == State.STATE_COMPLETING) || (current == State.STATE_COMPLETED))
+ if (current == State.STATE_COMPLETING) {
+ // ok we need to write the participant details to disk because it has just completed
+ BAParticipantRecoveryRecord recoveryRecord = new BAParticipantRecoveryRecord(id, participant, true, coordinator);
+
+ if (!XTSBARecoveryManager.getRecoveryManager().writeParticipantRecoveryRecord(recoveryRecord)) {
+ // hmm, could not write entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.completed_1", new Object[] {id}) ;
+ }
+ // we need to fail this transaction
+ failRequired = true;
+ }
+ }
+ // recheck state before we decide whether we need to fail -- we might have been sent a cancel while
+ // writing the log
+
+ synchronized(this)
{
+ current = state ;
+ if (current == State.STATE_COMPLETED) {
+ if (!failRequired) {
+ // record the fact that we have persisted this object so later operations will delete
+ // the log record
+ persisted = true;
+ } else {
+ // we must force a fail but we don't have a log record to delete
+ changeState(State.STATE_FAILING_COMPLETING);
+ }
+ } else {
+ // we need to delete the log record here as the cancel would not have known it was persisted
+ deleteRequired = true;
+ }
+ }
+
+
+ // check to see if we need to send a fail or delete the log record before going ahead to complete
+
+ if (failRequired) {
+ current = fail(BusinessActivityConstants.WSBA_ELEMENT_FAIL_QNAME);
+ } else if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.completed_2", new Object[] {id}) ;
+ }
+ }
+ } else if ((current == State.STATE_COMPLETING) || (current == State.STATE_COMPLETED)) {
sendCompleted() ;
}
@@ -899,30 +997,67 @@
/**
* Execute the cancel transition.
*
- * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1] - Unexpected exception from participant cancel
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1] - Faulted exception from participant cancel for WS-BA participant {0}
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_2 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_2] - Unexpected exception from participant cancel for WS-BA participant {0}
*/
- private void executeCancel()
+ private void executeCancel(boolean duringComplete)
{
+ boolean failRequired = false;
+
+ // TODO -- there is a potential race here with a completing thread
+ // the state diagrams in the spec say that if a cancel comes in while completing we have to cancel
+ // but the participant may be part way through executing a complete. strictly, that's something
+ // the participant has to deal with not us
try
{
participant.cancel() ;
}
+ catch (final FaultedException fe)
+ {
+ if (WSTLogger.arjLoggerI18N.isDebugEnabled())
+ {
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1", new Object[] { id}, fe) ;
+ }
+ // fail here because the participant doesn't want to retry the cancel
+ fail(BusinessActivityConstants.WSBA_ELEMENT_FAIL_QNAME);
+ }
catch (final Throwable th)
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCancel_2", new Object[] { id}, th) ;
}
- return ;
+ /*
+ * we can get here from state ACTIVE or CONPLETING. we could roll back the state as though the cancel
+ * never happened. the only problem is that coming from state COMPLETING we don't know whether the
+ * completing thread has logged its state or logged it and then deleted it because it saw the transition
+ * to CANCELING. so we roll back to ACTIVE but fail if we have come from COMPLETING
+ */
+ synchronized (this) {
+ if (state == State.STATE_CANCELING) {
+ if (duringComplete) {
+ failRequired = true;
+ changeState(State.STATE_FAILING_CANCELING);
+ } else {
+ changeState(State.STATE_ACTIVE);
+ return;
+ }
+ }
+ }
}
- sendCancelled() ;
- ended() ;
+ if (failRequired) {
+ fail(BusinessActivityConstants.WSBA_ELEMENT_FAIL_QNAME);
+ } else {
+ sendCancelled() ;
+ ended() ;
+ }
}
/**
* Execute the close transition.
*
- * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_1 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_1] - Unexpected exception from participant close
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_1 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_1] - Unexpected exception from participant close for WS-BA participant {0}
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_2 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_2] - Unable to delete recovery record during close for WS-BA participant {0}
*/
private void executeClose()
{
@@ -936,8 +1071,32 @@
{
WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeClose_1", th) ;
}
+ // restore previous state so we can retry the close otherwise we get stuck in state closing forever
+ changeState(State.STATE_COMPLETED);
+
+ sendCompleted();
return ;
}
+ // delete any log record for the participant
+ if (persisted) {
+ // if we cannot delete the participant record we effectively drop the close message
+ // here in the hope that we have better luck next time..
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- leave it so we can maybe retry later
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_2", new Object[] {id}) ;
+ }
+ // restore previous state so we can retry the close otherwise we get stuck in state closing forever
+
+ changeState(State.STATE_COMPLETED);
+
+ sendCompleted();
+
+ return;
+ }
+ }
+
sendClosed() ;
ended() ;
}
@@ -945,7 +1104,9 @@
/**
* Execute the compensate transition.
*
- * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1] - Unexpected exception from participant compensate
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1] - Faulted exception from participant compensate for WS-BA participant {0}
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_2 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_2] - Unexpected exception from participant compensate for WS-BA participant {0}
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_3 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_3] - Unable to delete recovery record during compensate for WS-BA participant {0}
*/
private void executeCompensate()
{
@@ -955,7 +1116,12 @@
}
catch (final FaultedException fe)
{
- fail(ArjunaTXConstants.WSARJTX_ELEMENT_FAULTED_QNAME) ;
+ if (WSTLogger.arjLoggerI18N.isDebugEnabled())
+ {
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1", new Object[] { id}, fe) ;
+ }
+ // fail here because the participant doesn't want to retry the compensate
+ fail(BusinessActivityConstants.WSBA_ELEMENT_FAIL_QNAME) ;
}
catch (final Throwable th)
{
@@ -975,21 +1141,41 @@
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeCompensate_2", new Object[] { id }, th) ;
}
return ;
}
final State current ;
+ boolean failRequired = false;
synchronized (this)
{
current = state ;
+ // need to do this while synchronized so no fail calls can get in on between
+
if (current == State.STATE_COMPENSATING)
{
- ended() ;
+ if (persisted) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // we have to fail since we don't want to run the compensate method again
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_3", new Object[] {id}) ;
+ }
+ failRequired = true;
+ changeState(State.STATE_FAILING_COMPENSATING);
+ }
+ }
+ // if we did not fail then we can decommission the participant now avoiding any further races
+ // we will send the compensate after we exit the synchronized block
+ if (!failRequired) {
+ ended();
+ }
}
}
- if (current == State.STATE_COMPENSATING)
+ if (failRequired) {
+ fail(BusinessActivityConstants.WSBA_ELEMENT_FAIL_QNAME);
+ } else if (current == State.STATE_COMPENSATING)
{
sendCompensated() ;
}
@@ -998,7 +1184,8 @@
/**
* Execute the complete transition.
*
- * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1] - Unexpected exception from participant compensate
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1] - Unexpected exception from participant complete for WS-BA participant {0}
+ * @message com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_2 [com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_2] - Unable to write log record during participant complete for WS-BA participant {0}
*/
private void executeComplete()
{
@@ -1010,12 +1197,14 @@
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine.executeComplete_1", new Object[] {id}, th) ;
}
return ;
}
- final State current ;
+ State current ;
+ boolean failRequired = false;
+ boolean deleteRequired = false;
synchronized (this)
{
current = state ;
@@ -1026,6 +1215,56 @@
}
if (current == State.STATE_COMPLETING)
{
+ // ok we need to write the participant details to disk because it has just completed
+ BAParticipantRecoveryRecord recoveryRecord = new BAParticipantRecoveryRecord(id, participant, true, coordinator);
+
+ if (!XTSBARecoveryManager.getRecoveryManager().writeParticipantRecoveryRecord(recoveryRecord)) {
+ // hmm, could not write entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeComplete_2", new Object[] {id}) ;
+ }
+ // we need to fail this transaction
+ failRequired = true;
+ }
+ }
+
+ // recheck state before we decide whether we need to fail -- we might have been sent a cancel while
+ // writing the log
+
+ synchronized(this)
+ {
+ current = state ;
+ if (current == State.STATE_COMPLETED) {
+ if (!failRequired) {
+ // record the fact that we have persisted this object so later operations will delete
+ // the log record
+ persisted = true;
+ } else {
+ // we must force a fail but we don't have a log record to delete
+ changeState(State.STATE_FAILING_COMPLETING);
+ }
+ } else {
+ // we cannot force a fail now so just delete
+ failRequired = false;
+ // we need to delete the log record here as the cancel would not have known it was persisted
+ deleteRequired = true;
+ }
+ }
+
+ // check to see if we need to send a fail or delete the log record before going ahead to complete
+
+ if (failRequired) {
+ current = fail(BusinessActivityConstants.WSBA_ELEMENT_FAIL_QNAME);
+ } else if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.completed_2", new Object[] {id}) ;
+ }
+ }
+ } else if ((current == State.STATE_COMPLETING) || (current == State.STATE_COMPLETED)) {
sendCompleted() ;
}
}
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/ParticipantCompletionCoordinatorEngine.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/ParticipantCompletionCoordinatorEngine.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/ParticipantCompletionCoordinatorEngine.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -68,6 +68,10 @@
* The current state.
*/
private State state ;
+ /**
+ * The flag indicating that this coordinator has been recovered from the log.
+ */
+ private boolean recovered ;
/**
* Construct the initial engine for the coordinator.
@@ -76,7 +80,7 @@
*/
public ParticipantCompletionCoordinatorEngine(final String id, final W3CEndpointReference participant)
{
- this(id, participant, State.STATE_ACTIVE) ;
+ this(id, participant, State.STATE_ACTIVE, false) ;
}
/**
@@ -86,12 +90,13 @@
* @param state The initial state.
*/
public ParticipantCompletionCoordinatorEngine(final String id, final W3CEndpointReference participant,
- final State state)
+ final State state, final boolean recovered)
{
this.id = id ;
this.instanceIdentifier = new InstanceIdentifier(id) ;
this.participant = participant ;
this.state = state ;
+ this.recovered = recovered;
}
/**
@@ -101,7 +106,13 @@
public void setCoordinator(final BAParticipantManager coordinator)
{
this.coordinator = coordinator ;
- ParticipantCompletionCoordinatorProcessor.getProcessor().activateCoordinator(this, id) ;
+ // unrecovered participants are always activated
+ // we only need to reactivate recovered participants which were successfully COMPLETED or which began
+ // CLOSING. any others will only have been saved because of a heuristic outcome. we can safely drop
+ // them since we implement presumed abort.
+ if (!recovered || state == State.STATE_COMPLETED || state == State.STATE_CLOSING) {
+ ParticipantCompletionCoordinatorProcessor.getProcessor().activateCoordinator(this, id) ;
+ }
}
/**
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/ParticipantCompletionParticipantEngine.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/ParticipantCompletionParticipantEngine.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/messaging/engines/ParticipantCompletionParticipantEngine.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -23,7 +23,6 @@
import com.arjuna.webservices.SoapFault;
import com.arjuna.webservices.logging.WSTLogger;
import com.arjuna.webservices.util.TransportTimer;
-import com.arjuna.webservices.wsarjtx.ArjunaTXConstants;
import com.arjuna.webservices11.wsaddr.AddressingHelper;
import com.arjuna.webservices11.wsarj.ArjunaContext;
import com.arjuna.webservices11.wsarj.InstanceIdentifier;
@@ -32,12 +31,13 @@
import com.arjuna.webservices11.wsba.BusinessActivityConstants;
import com.arjuna.webservices11.wsba.client.ParticipantCompletionCoordinatorClient;
import com.arjuna.webservices11.wsba.processors.ParticipantCompletionParticipantProcessor;
-import com.arjuna.webservices11.ServiceRegistry;
import com.arjuna.wsc11.messaging.MessageId;
import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
import com.arjuna.wst.FaultedException;
import org.oasis_open.docs.ws_tx.wsba._2006._06.NotificationType;
import org.oasis_open.docs.ws_tx.wsba._2006._06.StatusType;
+import org.jboss.jbossts.xts11.recovery.participant.ba.BAParticipantRecoveryRecord;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
import javax.xml.namespace.QName;
import javax.xml.ws.addressing.AddressingProperties;
@@ -74,8 +74,17 @@
* The associated timer task or null.
*/
private TimerTask timerTask ;
+ /**
+ * true if this participant has been recovered otherwise false
+ */
+ private boolean recovered;
/**
+ * true if this participant's recovery details have been logged to disk otherwise false
+ */
+ private boolean persisted;
+
+ /**
* Construct the initial engine for the participant.
* @param id The participant id.
* @param coordinator The coordinator endpoint reference.
@@ -84,7 +93,7 @@
public ParticipantCompletionParticipantEngine(final String id, final W3CEndpointReference coordinator,
final BusinessAgreementWithParticipantCompletionParticipant participant)
{
- this(id, coordinator, participant, State.STATE_ACTIVE) ;
+ this(id, coordinator, participant, State.STATE_ACTIVE, false) ;
}
/**
@@ -93,15 +102,18 @@
* @param coordinator The coordinator endpoint reference.
* @param participant The participant.
* @param state The initial state.
+ * @param recovered true if the engine has been recovered from th elog otherwise false
*/
public ParticipantCompletionParticipantEngine(final String id, final W3CEndpointReference coordinator,
- final BusinessAgreementWithParticipantCompletionParticipant participant, final State state)
+ final BusinessAgreementWithParticipantCompletionParticipant participant, final State state, boolean recovered)
{
this.id = id ;
this.instanceIdentifier = new InstanceIdentifier(id) ;
this.coordinator = coordinator ;
this.participant = participant ;
this.state = state ;
+ this.recovered = recovered;
+ this.persisted = recovered;
}
/**
@@ -126,7 +138,7 @@
{
final State current ;
synchronized(this)
- {
+ {
current = state ;
if (current == State.STATE_ACTIVE)
{
@@ -300,19 +312,33 @@
* NotCompleting -> NotCompleting (invalid state)
* Exiting -> Exiting (invalid state)
* Ended -> Ended
+ * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.failed_1 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.failed_1] - Unable to delete recovery record during failed for WS-BA participant {0}
*/
public void failed(final NotificationType failed, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
final State current ;
+ boolean deleteRequired = false;
synchronized(this)
{
current = state ;
if ((current == State.STATE_FAILING_ACTIVE) || (current == State.STATE_FAILING_CANCELING) ||
(current == State.STATE_FAILING_COMPENSATING))
{
+ deleteRequired = persisted;
ended() ;
}
}
+ // if we just ended the participant ensure any log record gets deleted
+
+ if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- nothing more we can do than log a message
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.failed_1", new Object[] {id}) ;
+ }
+ }
+ }
}
/**
@@ -380,15 +406,31 @@
* @param soapFault The soap fault.
* @param addressingProperties The addressing context.
* @param arjunaContext The arjuna context.
+ * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.soapFault_1 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.soapFault_1] - Unable to delete recovery record during soapFault processing for WS-BA participant {0}
*/
public void soapFault(final SoapFault soapFault, final AddressingProperties addressingProperties, final ArjunaContext arjunaContext)
{
- ended() ;
- try
- {
- participant.error() ;
- }
- catch (final Throwable th) {} // ignore
+ boolean deleteRequired;
+ synchronized(this) {
+ deleteRequired = persisted;
+ ended() ;
+ }
+ // TODO -- clarify when and why this gets called and update doc in interface. also check unknown()
+ try
+ {
+ participant.error() ;
+ }
+ catch (final Throwable th) {} // ignore
+ // if we just ended the participant ensure any log record gets deleted
+ if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- nothing more we can do than log a message
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.soapFault_1", new Object[] {id}) ;
+ }
+ }
+ }
}
/**
@@ -405,10 +447,14 @@
* NotCompleting -> NotCompleting (invalid state)
* Exiting -> Exiting (invalid state)
* Ended -> Ended (invalid state)
+ * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.completed_1 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.completed_1] - Unable to write recovery record during completed for WS-BA participant {0}
+ * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.completed_2 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.completed_2] - Unable to delete recovery record during completed for WS-BA participant {0}
*/
public State completed()
{
- final State current ;
+ State current ;
+ boolean failRequired = false;
+ boolean deleteRequired = false;
synchronized(this)
{
current = state ;
@@ -418,8 +464,56 @@
}
}
- if ((current == State.STATE_ACTIVE) || (current == State.STATE_COMPLETED))
+ if (current == State.STATE_ACTIVE) {
+ // ok we need to write the participant details to disk because it has just completed
+ BAParticipantRecoveryRecord recoveryRecord = new BAParticipantRecoveryRecord(id, participant, true, coordinator);
+
+ if (!XTSBARecoveryManager.getRecoveryManager().writeParticipantRecoveryRecord(recoveryRecord)) {
+ // hmm, could not write entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.completed_1", new Object[] {id}) ;
+ }
+ // we need to fail this transaction
+ failRequired = true;
+ }
+ }
+ // recheck state before we decide whether we need to fail -- we might have been sent a cancel while
+ // writing the log
+
+ synchronized(this)
{
+ current = state ;
+ if (current == State.STATE_COMPLETED) {
+ if (!failRequired) {
+ // record the fact that we have persisted this object so later operations will delete
+ // the log record
+ persisted = true;
+ } else {
+ // we must force a fail but we don't have a log record to delete
+ changeState(State.STATE_FAILING_ACTIVE);
+ }
+ } else {
+ // we cannot force a fail now so just delete
+ failRequired = false;
+ // we need to delete the log record here as the cancel would not have known it was persisted
+ deleteRequired = true;
+ }
+ }
+
+ // check to see if we need to send a fail or delete the log record before going ahead to complete
+
+ if (failRequired) {
+ current = fail(BusinessActivityConstants.WSBA_ELEMENT_FAIL_QNAME);
+ } else if (deleteRequired) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry log warning
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.completed_2", new Object[] {id}) ;
+ }
+ }
+ } else if ((current == State.STATE_ACTIVE) || (current == State.STATE_COMPLETED)) {
sendCompleted() ;
}
@@ -812,7 +906,8 @@
/**
* Execute the cancel transition.
*
- * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1] - Unexpected exception from participant cancel
+ * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1] - Faulted exception from participant cancel for WS-BA participant {0}
+ * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_2 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_2] - Unexpected exception from participant cancel for WS-BA participant {0}
*/
private void executeCancel()
{
@@ -820,12 +915,31 @@
{
participant.cancel() ;
}
+ catch (final FaultedException fe)
+ {
+ if (WSTLogger.arjLoggerI18N.isDebugEnabled())
+ {
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1", new Object[] { id}, fe) ;
+ }
+ // fail here because the participant doesn't want to retry the cancel
+ fail(BusinessActivityConstants.WSBA_ELEMENT_FAIL_QNAME);
+ return;
+ }
catch (final Throwable th)
{
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCancel_2", new Object[] { id}, th) ;
}
+ /*
+ * we only get here in from state ACTIVE so if we are stll in state CANCELING then roll back the
+ * state allowing a retry of the cancel
+ */
+ synchronized (this) {
+ if (state == State.STATE_CANCELING) {
+ changeState(State.STATE_ACTIVE);
+ }
+ }
return ;
}
sendCancelled() ;
@@ -835,7 +949,8 @@
/**
* Execute the close transition.
*
- * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1] - Unexpected exception from participant close
+ * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1] - Unexpected exception from participant close for WS-BA participant {0}
+ * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_2 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_2] - Unable to delete recovery record during close for WS-BA participant {0}
*/
private void executeClose()
{
@@ -849,8 +964,31 @@
{
WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1", th) ;
}
+ // restore previous state so we can retry the close otherwise we get stuck in state closing forever
+ changeState(State.STATE_COMPLETED);
+
+ sendCompleted();
return ;
}
+ // delete any log record for the participant
+ if (persisted) {
+ // if we cannot delete the participant record we effectively drop the close message
+ // here in the hope that we have better luck next time..
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // hmm, could not delete entry -- leave it so we can maybe retry later
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_2", new Object[] {id}) ;
+ }
+ // restore previous state so we can retry the close otherwise we get stuck in state closing forever
+
+ changeState(State.STATE_COMPLETED);
+
+ sendCompleted();
+
+ return;
+ }
+ }
sendClosed() ;
ended() ;
}
@@ -858,7 +996,9 @@
/**
* Execute the compensate transition.
*
- * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_1 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_1] - Unexpected exception from participant compensate
+ * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_1 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_1] - Faulted exception from participant compensate for WS-BA participant {0}
+ * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_2 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_2] - Unexpected exception from participant compensate for WS-BA participant {0}
+ * @message com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_3 [com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_3] - Unable to delete recovery record during compensate for WS-BA participant {0}
*/
private void executeCompensate()
{
@@ -868,8 +1008,13 @@
}
catch (final FaultedException fe)
{
- // TODO - use the right value here -- shoudl be a QName for Faulted (Kev's 1.1 code uses JBOSSTX.FAULTED_ERROR_CODE_QNAME)
- fail(ArjunaTXConstants.WSARJTX_ELEMENT_FAULTED_QNAME);
+ if (WSTLogger.arjLoggerI18N.isDebugEnabled())
+ {
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_1", new Object[] { id}, fe) ;
+ }
+ // fail here because the participant doesn't want to retry the compensate
+ fail(BusinessActivityConstants.WSBA_ELEMENT_FAIL_QNAME);
+ return;
}
catch (final Throwable th)
{
@@ -889,22 +1034,41 @@
if (WSTLogger.arjLoggerI18N.isDebugEnabled())
{
- WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeClose_1", th) ;
+ WSTLogger.arjLoggerI18N.debug("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_2", new Object[] { id }, th) ;
}
return ;
}
final State current ;
+ boolean failRequired = false;
synchronized (this)
{
current = state ;
+ // need to do this while synchronized so no fail calls can get in on between
+
if (current == State.STATE_COMPENSATING)
{
- ended() ;
+ if (persisted) {
+ if (!XTSBARecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
+ // we have to fail since we don't want to run the compensate method again
+ if (WSTLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ WSTLogger.arjLoggerI18N.warn("com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine.executeCompensate_3", new Object[] {id}) ;
+ }
+ failRequired = true;
+ changeState(State.STATE_FAILING_COMPENSATING);
+ }
+ }
+ // if we did not fail then we can decommission the participant now avoiding any further races
+ // we will send the compensate after we exit the synchronized block
+ if (!failRequired) {
+ ended();
+ }
}
}
- if (current == State.STATE_COMPENSATING)
- {
+ if (failRequired) {
+ fail(BusinessActivityConstants.WSBA_ELEMENT_FAIL_QNAME);
+ } else if (current == State.STATE_COMPENSATING) {
sendCompensated() ;
}
}
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/stub/BusinessAgreementWithCoordinatorCompletionStub.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/stub/BusinessAgreementWithCoordinatorCompletionStub.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/stub/BusinessAgreementWithCoordinatorCompletionStub.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -47,8 +47,10 @@
import com.arjuna.wst.SystemException;
import com.arjuna.wst.WrongStateException;
import com.arjuna.wst11.messaging.engines.CoordinatorCompletionCoordinatorEngine;
+import com.arjuna.wst11.RecoverableBusinessAgreementWithCoordinatorCompletionParticipant;
+import com.arjuna.wst11.BAParticipantManager;
-public class BusinessAgreementWithCoordinatorCompletionStub implements BusinessAgreementWithCoordinatorCompletionParticipant, PersistableParticipant
+public class BusinessAgreementWithCoordinatorCompletionStub implements RecoverableBusinessAgreementWithCoordinatorCompletionParticipant, PersistableParticipant
{
private static final QName QNAME_BACCWS_PARTICIPANT = new QName("baccwsParticipant") ;
private CoordinatorCompletionCoordinatorEngine participant ;
@@ -59,6 +61,14 @@
this.participant = participant ;
}
+ /**
+ * donstructor for use during recovery
+ */
+ public BusinessAgreementWithCoordinatorCompletionStub()
+ {
+ this.participant = null ;
+ }
+
public synchronized void close ()
throws WrongStateException, SystemException
{
@@ -221,7 +231,7 @@
StreamHelper.writeEndElement(writer, null, null) ;
writer.close() ;
- oos.packString(writer.toString()) ;
+ oos.packString(sw.toString()) ;
final State state = participant.getStatus();
final QName stateName = state.getValue();
@@ -253,7 +263,7 @@
final XMLStreamReader reader = SoapUtils.getXMLStreamReader(new StringReader(eprValue)) ;
StreamHelper.checkNextStartTag(reader, QNAME_BACCWS_PARTICIPANT) ;
- String eprefText = reader.getText();
+ String eprefText = reader.getElementText();
StreamSource source = new StreamSource(new StringReader(eprefText));
final W3CEndpointReference endpointReference = new W3CEndpointReference(source);
@@ -270,7 +280,15 @@
QName statename = new QName(ns, localPart, prefix);
State state = State.toState11(statename);
- participant = new CoordinatorCompletionCoordinatorEngine(id, endpointReference, state);
+ participant = new CoordinatorCompletionCoordinatorEngine(id, endpointReference, state, true);
+ /*
+ * this hppens when the registrar first creates an engine -- we need to do something similar
+ BusinessAgreementWithParticipantCompletionImple participant = new BusinessAgreementWithParticipantCompletionImple(
+ new BusinessAgreementWithParticipantCompletionStub(engine), id);
+ engine.setCoordinator(participant.participantManager()) ;
+
+ _coordManager.enlistParticipant(participant);
+ */
return true ;
}
catch (final Throwable th)
@@ -279,4 +297,13 @@
return false ;
}
}
+
+ /**
+ * establish a back channel from the coordinator side protocol engine to the coordinator.
+ *
+ * @param participantManager a manager which will forward incoming remote participant requests to the coordinator
+ */
+ public void setParticipantManager(BAParticipantManager participantManager) {
+ participant.setCoordinator(participantManager);
+ }
}
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/stub/BusinessAgreementWithParticipantCompletionStub.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/stub/BusinessAgreementWithParticipantCompletionStub.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/com/arjuna/wst11/stub/BusinessAgreementWithParticipantCompletionStub.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -47,8 +47,10 @@
import com.arjuna.wst.SystemException;
import com.arjuna.wst.WrongStateException;
import com.arjuna.wst11.messaging.engines.ParticipantCompletionCoordinatorEngine;
+import com.arjuna.wst11.RecoverableBusinessAgreementWithParticipantCompletionParticipant;
+import com.arjuna.wst11.BAParticipantManager;
-public class BusinessAgreementWithParticipantCompletionStub implements BusinessAgreementWithParticipantCompletionParticipant, PersistableParticipant
+public class BusinessAgreementWithParticipantCompletionStub implements RecoverableBusinessAgreementWithParticipantCompletionParticipant, PersistableParticipant
{
private static final QName QNAME_BAPCWS_PARTICIPANT = new QName("bapcwsParticipant") ;
@@ -60,6 +62,15 @@
this.participant = participant ;
}
+ /**
+ * constructor for use during recovery
+ * @throws Exception
+ */
+ public BusinessAgreementWithParticipantCompletionStub()
+ {
+ this.participant = null ;
+ }
+
public synchronized void close ()
throws WrongStateException, SystemException
{
@@ -88,7 +99,7 @@
}
public synchronized void cancel ()
- throws WrongStateException, SystemException
+ throws FaultedException, WrongStateException, SystemException
{
/*
* Active -> illegal state
@@ -108,6 +119,10 @@
{
throw new SystemException() ;
}
+ else if (state != State.STATE_FAILING_CANCELING)
+ {
+ throw new FaultedException() ;
+ }
else if (state != State.STATE_ENDED)
{
throw new WrongStateException() ;
@@ -181,7 +196,7 @@
StreamHelper.writeEndElement(writer, null, null) ;
writer.close() ;
- oos.packString(writer.toString()) ;
+ oos.packString(sw.toString()) ;
final State state = participant.getStatus();
final QName stateName = state.getValue();
@@ -214,7 +229,7 @@
// this should successfully reverse the save process
final XMLStreamReader reader = SoapUtils.getXMLStreamReader(new StringReader(eprValue)) ;
StreamHelper.checkNextStartTag(reader, QNAME_BAPCWS_PARTICIPANT) ;
- String eprefText = reader.getText();
+ String eprefText = reader.getElementText();
StreamSource source = new StreamSource(new StringReader(eprefText));
final W3CEndpointReference endpointReference = new W3CEndpointReference(source);
@@ -231,7 +246,25 @@
QName statename = new QName(ns, localPart, prefix);
State state = State.toState11(statename);
- participant = new ParticipantCompletionCoordinatorEngine(id, endpointReference, state);
+ participant = new ParticipantCompletionCoordinatorEngine(id, endpointReference, state, true);
+ // TODO -- work out how to fix this
+ // we need to obtain a participant manager allowing the engine to post events to the coordinator
+ // the normal one is obtained from the BusinessAgreementWithParticipantCompletionImple which wraps
+ // this stub. the registrar normally creates an engine, a stub and a BAWCPImple. it asks the BAWPCImple
+ // for a participant manager and passes it to the engine. to make things worse, the BAWPCImple relies
+ // upon being created inside an activity which identifies the relevant coordinator. this is because
+ // the manager it creates needs to access the coordinator and it uses the current activity to provide
+ // an access path. we cannot do this here because 1) the restore_state protocol is a simple top-down
+ // recursive model so does not give us a handle on either the enclosing BAWCImple or the ACCoordinator
+ // and 2) the BAWCImple does not know about the implementation of this stub so cannot access the engine
+ // it wraps and 3) we cannot install an activity at this point during recovery as we are still creating
+ // the coordinator which will coordinate it.
+ //
+ // if we obtain a handle on the current coordinator we can just create an alternative proxy to route
+ // messages directly, avoiding the activity hierarchy.
+ //
+ //participantManager = ???;
+ // participant.setCoordinator(participantManager) ;
return true ;
}
catch (final Throwable th)
@@ -240,4 +273,13 @@
return false ;
}
}
+
+ /**
+ * establish a back channel from the coordinator side protocol engine to the coordinator.
+ *
+ * @param participantManager a manager which will forward incoming remote participant requests to the coordinator
+ */
+ public void setParticipantManager(BAParticipantManager participantManager) {
+ participant.setCoordinator(participantManager);
+ }
}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/WS-T/dev/src11/org/jboss/jbossts/xts11/recovery/participant/ba/BAParticipantRecoveryRecord.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src11/org/jboss/jbossts/xts11/recovery/participant/ba/BAParticipantRecoveryRecord.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src11/org/jboss/jbossts/xts11/recovery/participant/ba/BAParticipantRecoveryRecord.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,95 @@
+package org.jboss.jbossts.xts11.recovery.participant.ba;
+
+import com.arjuna.ats.arjuna.state.OutputObjectState;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+import com.arjuna.webservices11.wsba.State;
+import com.arjuna.webservices11.wsba.processors.ParticipantCompletionParticipantProcessor;
+import com.arjuna.webservices11.wsba.processors.CoordinatorCompletionParticipantProcessor;
+import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
+import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
+import com.arjuna.wst11.messaging.engines.ParticipantCompletionParticipantEngine;
+import com.arjuna.wst11.messaging.engines.CoordinatorCompletionParticipantEngine;
+
+import javax.xml.ws.wsaddressing.W3CEndpointReference;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import java.io.IOException;
+import java.io.StringReader;
+
+/**
+ * recovery record specific to WS-AT 1.1 protocol participants. this implements the behaviours
+ * necessary to save and restore a 1.1 participant to or from the TX object store
+ */
+public class BAParticipantRecoveryRecord extends org.jboss.jbossts.xts.recovery.participant.ba.BAParticipantRecoveryRecord {
+
+ /**
+ * constructor used during prepare processing to create an AT 1.1 participant record
+ * for saving to the object store
+ * @param id the id of the application-specific participant
+ * @param participant the application-specific participant
+ * @param
+ */
+ public BAParticipantRecoveryRecord(String id, BusinessAgreementWithParticipantCompletionParticipant participant, boolean isParticipantCompletion, W3CEndpointReference endpoint)
+ {
+ super(id, participant, isParticipantCompletion);
+ this.endpoint = endpoint;
+ }
+
+ /**
+ * constructor used during recovery processing to create a record whose contents will be
+ * recovered from the object store
+ */
+ public BAParticipantRecoveryRecord()
+ {
+ super(null, null, false);
+ }
+
+ /**
+ * save the endpoint reference to the coordinator for this participant
+ */
+ protected void saveEndpointReference(OutputObjectState oos) throws IOException {
+ // the toString method will do what we need
+ oos.packString(endpoint.toString());
+ }
+
+ /**
+ * restore the endpoint reference to the coordinator for this participant
+ */
+ protected void restoreEndpointReference(InputObjectState ios) throws IOException {
+ String endpointString = ios.unpackString();
+ Source source = new StreamSource(new StringReader(endpointString));
+ endpoint = new W3CEndpointReference(source);
+ }
+
+ /**
+ * create a participant engine to manage commit or rollback processing for the
+ * participant and install it in the active participants table
+ */
+ public void activate() {
+ if (isParticipantCompletion) {
+ ParticipantCompletionParticipantEngine engine = new ParticipantCompletionParticipantEngine(id, endpoint, participant, State.STATE_COMPLETED, true);
+ ParticipantCompletionParticipantProcessor.getProcessor().activateParticipant(engine, getId());
+ } else {
+ BusinessAgreementWithCoordinatorCompletionParticipant coordinatorCompletionParticipant = (BusinessAgreementWithCoordinatorCompletionParticipant) participant;
+ CoordinatorCompletionParticipantEngine engine = new CoordinatorCompletionParticipantEngine(id, endpoint, coordinatorCompletionParticipant, State.STATE_COMPLETED, true);
+ CoordinatorCompletionParticipantProcessor.getProcessor().activateParticipant(engine, getId());
+ }
+ }
+
+ /**
+ * test whether a participant is currently activated with the id of this recovery record.
+ *
+ * @return true if a participant is currently activated with the id of this recovery record
+ */
+ public boolean isActive()
+ {
+ if (isParticipantCompletion) {
+ return ParticipantCompletionParticipantProcessor.getProcessor().isActive(getId());
+ } else {
+ return CoordinatorCompletionParticipantProcessor.getProcessor().isActive(getId());
+ }
+ }
+
+
+ private W3CEndpointReference endpoint;
+}
\ No newline at end of file
Modified: labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst/tests/junit/TestCoordinatorCompletionParticipantProcessor.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst/tests/junit/TestCoordinatorCompletionParticipantProcessor.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst/tests/junit/TestCoordinatorCompletionParticipantProcessor.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -83,6 +83,15 @@
public void deactivateParticipant(CoordinatorCompletionParticipantInboundEvents participant) {
}
+ /**
+ * Check whether a participant with the given id is currently active
+ *
+ * @param identifier The identifier.
+ */
+ public boolean isActive(String identifier) {
+ return false;
+ }
+
public void cancel(NotificationType cancel, AddressingContext addressingContext, ArjunaContext arjunaContext)
{
final String messageId = addressingContext.getMessageID().getValue() ;
Modified: labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst/tests/junit/TestParticipantCompletionParticipantProcessor.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst/tests/junit/TestParticipantCompletionParticipantProcessor.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst/tests/junit/TestParticipantCompletionParticipantProcessor.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -83,6 +83,15 @@
public void deactivateParticipant(ParticipantCompletionParticipantInboundEvents participant) {
}
+ /**
+ * Check whether a participant with the given id is currently active
+ *
+ * @param identifier The identifier.
+ */
+ public boolean isActive(String identifier) {
+ return false;
+ }
+
public void cancel(NotificationType cancel, AddressingContext addressingContext, ArjunaContext arjunaContext)
{
final String messageId = addressingContext.getMessageID().getValue() ;
Modified: labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst11/tests/junit/TestCoordinatorCompletionParticipantProcessor.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst11/tests/junit/TestCoordinatorCompletionParticipantProcessor.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst11/tests/junit/TestCoordinatorCompletionParticipantProcessor.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -84,6 +84,15 @@
public void deactivateParticipant(CoordinatorCompletionParticipantInboundEvents participant) {
}
+ /**
+ * Check whether a participant with the given id is currently active
+ *
+ * @param identifier The identifier.
+ */
+ public boolean isActive(String identifier) {
+ return false;
+ }
+
public void cancel(NotificationType cancel, AddressingProperties addressingProperties, ArjunaContext arjunaContext)
{
final String messageId = addressingProperties.getMessageID().getURI().toString() ;
Modified: labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst11/tests/junit/TestParticipantCompletionParticipantProcessor.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst11/tests/junit/TestParticipantCompletionParticipantProcessor.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WS-T/tests/src/com/arjuna/wst11/tests/junit/TestParticipantCompletionParticipantProcessor.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -84,6 +84,15 @@
public void deactivateParticipant(ParticipantCompletionParticipantInboundEvents participant) {
}
+ /**
+ * Check whether a participant with the given id is currently active
+ *
+ * @param identifier The identifier.
+ */
+ public boolean isActive(String identifier) {
+ return false;
+ }
+
public void cancel(NotificationType cancel, AddressingProperties addressingProperties, ArjunaContext arjunaContext)
{
final String messageId = addressingProperties.getMessageID().getURI().toString() ;
Modified: labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/api/CoordinatorManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/api/CoordinatorManager.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/api/CoordinatorManager.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -68,7 +68,7 @@
* @exception SystemException Thrown if any other error occurs.
*/
- public void enlistParticipant (Participant act) throws NoActivityException, WrongStateException, DuplicateParticipantException, InvalidParticipantException, SystemException;
+ public void enlistParticipant (RecoverableParticipant act) throws NoActivityException, WrongStateException, DuplicateParticipantException, InvalidParticipantException, SystemException;
/**
* Remove the specified participant from the coordinator associated with
@@ -105,7 +105,8 @@
/**
* A participant has faulted during normal execution or compensation.
* The saga will attempt to undo. The WS-T specification is a little
- * vague here - we assume the entire transaction has to undo.
+ * vague here - we assume the entire transaction has to undo and a heuristic
+ * hazard needs to be logged.
*
* @param participantId The participant.
*
@@ -117,5 +118,20 @@
public void participantFaulted (String participantId) throws NoActivityException, InvalidParticipantException, SystemException;
+ /**
+ * A participant cannot complete during normal execution or compensation.
+ * The saga will attempt to undo. The WS-T specification is a little
+ * vague here - we assume the entire transaction has to undo.
+ *
+ * @param participantId The participant.
+ *
+ * @exception NoActivityException Thrown if there is no activity associated
+ * with the current thread.
+ * @exception InvalidParticipantException Thrown if the participant is invalid.
+ * @exception SystemException Thrown if any other error occurs.
+ */
+
+ public void participantCannotComplete (String participantId) throws NoActivityException, InvalidParticipantException, WrongStateException, SystemException;
+
}
Copied: labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/exceptions/CancelFailedException.java (from rev 23805, labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/exceptions/CompensateFailedException.java)
===================================================================
--- labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/exceptions/CancelFailedException.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/exceptions/CancelFailedException.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,57 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2008
+ *
+ * $Id:$
+ */
+
+package com.arjuna.mw.wscf.model.sagas.exceptions;
+
+import com.arjuna.mw.wsas.exceptions.SystemException;
+
+
+/**
+ * A fail occurred during a Business Agreement cancel operation -- only applies in WSBA 1.1.
+ *
+ * @author Andrew Dinn(adinn at redhat.com)
+ * @version $Id:$
+ */
+
+public class CancelFailedException extends SystemException
+{
+
+ public CancelFailedException()
+ {
+ super();
+ }
+
+ public CancelFailedException(String s)
+ {
+ super(s);
+ }
+
+ public CancelFailedException(String s, int errorcode)
+ {
+ super(s, errorcode);
+ }
+
+}
\ No newline at end of file
Property changes on: labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/exceptions/CancelFailedException.java
___________________________________________________________________
Name: svn:mergeinfo
+
Added: labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/participants/RecoverableParticipant.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/participants/RecoverableParticipant.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/participants/RecoverableParticipant.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,16 @@
+package com.arjuna.mw.wscf.model.sagas.participants;
+
+import com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator;
+
+/**
+ * extension of the Participant API implemented by BA Participant Stubs which allows the coordinator
+ * to establish a back channel from the participant engine to the corodinator during recovery processing.
+ * the back channel is necessary so that remote participant requests can flow via the protocol engine to
+ * to the coordinator. the back channel is normally established using the activivty mechanism but this
+ * is not possible when recreating participant stubs and their engines during recovery as there is no thread
+ * association in place.
+ */
+public interface RecoverableParticipant extends Participant
+{
+ public void setCoordinator(ACCoordinator coordinator);
+}
Added: labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/participants/RecoverableParticipantWithComplete.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/participants/RecoverableParticipantWithComplete.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mw/wscf/model/sagas/participants/RecoverableParticipantWithComplete.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,13 @@
+package com.arjuna.mw.wscf.model.sagas.participants;
+
+/**
+ * extension of the ParticipantWithComplete API implemented by BA Participant Records which allows the coordinator
+ * to establish a back channel form the participant engine to the corodinator during recovery processing.
+ * the back channel is necessary so that remote participant requests can flow via the protocol engine to
+ * to the coordinator. the back channel is normally established using the activivty mechanism but this
+ * is not possible when recreating participants and their engines during recovery as there is no thread
+ * association in place.
+ */
+public interface RecoverableParticipantWithComplete extends RecoverableParticipant, ParticipantWithComplete
+{
+}
Modified: labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/ACCoordinator.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/ACCoordinator.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/ACCoordinator.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -53,8 +53,13 @@
/**
* This class represents a specific coordination instance. It is essentially an
- * ArjunaCore TwoPhaseCoordinator, which gives us access to two-phase with
- * synchronization support but without thread management.
+ * ArjunaCore BasicAction which gives us independent prepare, commit and abort
+ * without synchronization support and without thread management. We don't
+ * inherit from TwoPhaseCoordinator because we don't ever initiate a 2phase
+ * operation directly -- we map the BA Termination protocol complete message
+ * to prepare, the close message to commit and the cancel to abort so these
+ * operations are decoupled actions. Also, since we don't call end we cannot
+ * usefully use the synchronization support (not that we need it anyway).
*
* @author Mark Little (mark.little at arjuna.com)
* @version $Id: ACCoordinator.java,v 1.5 2005/05/19 12:13:37 nmcl Exp $
@@ -62,22 +67,32 @@
*
* @message com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_1
* [com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_1] -
- * Complete call failed!
+ * Cannot complete business activity until all ParticipantCompletion participants have completed
* @message com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_2
* [com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_2] -
* Null is an invalid parameter.
* @message com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_3
* [com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_3] -
* Wrong state for operation!
+ * @message com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_4
+ * [com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_4] -
+ * Complete of action-id {0} failed.
+ * @message com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_5
+ * [com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_5] - Received
+ * heuristic: {0} .
+ * @message com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_6
+ * [com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_6] - Action marked as AbortOnly {0}.
*/
-public class ACCoordinator extends TwoPhaseCoordinator
+public class ACCoordinator extends BasicAction
{
- private final static int DELISTED = 0;
+ private final static int DELISTED = 0;
- private final static int COMPLETED = 1;
+ private final static int COMPLETED = 1;
+ private final static int FAILED = 2;
+
public ACCoordinator ()
{
super();
@@ -101,10 +116,6 @@
* This implementation only supports coordination at the end of the
* activity.
*
- * @param CompletionStatus
- * cs The completion status to use when determining how to
- * execute the protocol.
- *
* @exception WrongStateException
* Thrown if the coordinator is in a state the does not allow
* coordination to occur.
@@ -123,67 +134,247 @@
return null;
}
- /**
- */
+ /**
+ * start the transaction
+ *
+ * @param parentAction
+ * @return
+ */
+ public int start (BasicAction parentAction)
+ {
+ if (parentAction != null)
+ parentAction.addChildAction(this);
- public synchronized void complete () throws WrongStateException,
- SystemException
- {
- if (status() == ActionStatus.RUNNING)
- {
- /*
- * Transaction is active, so we can look at the pendingList only.
- */
+ return super.Begin(parentAction);
+ }
- if (pendingList != null)
- {
- RecordListIterator iter = new RecordListIterator(pendingList);
- AbstractRecord absRec = iter.iterate();
+ /**
+ * ensure that none of required participants are still active throwing a SystemException if they are
+ * @param allParticipants true if no participants, including CooordinatorCompletion participants,
+ * may still be active and false if only CooordinatorCompletion participants may still be active.
+ * @throws SystemException if any of the required participants has not completed.
+ */
+ void ensureNotActive(boolean allParticipants) throws SystemException
+ {
+ if (pendingList != null)
+ {
+ RecordListIterator iter = new RecordListIterator(pendingList);
+ AbstractRecord absRec = iter.iterate();
- try
- {
- while (absRec != null)
- {
- if (absRec instanceof ParticipantRecord)
- {
- ParticipantRecord pr = (ParticipantRecord) absRec;
+ while (absRec != null)
+ {
+ if (absRec instanceof ParticipantRecord)
+ {
+ ParticipantRecord pr = (ParticipantRecord) absRec;
- if (!pr.complete())
- {
- preventCommit();
+ if ((allParticipants || pr.isParticipantCompletion()) && pr.isActive())
+ {
+ throw new SystemException(
+ wscfLogger.log_mesg
+ .getString("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_1"));
+ }
+ }
- throw new SystemException(
- wscfLogger.log_mesg
- .getString("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_1"));
- }
- }
+ absRec = iter.iterate();
+ }
+ }
+ }
- absRec = iter.iterate();
- }
- }
- catch (SystemException ex)
- {
- throw ex;
- }
- catch (Exception ex)
- {
- preventCommit();
+ /**
+ * 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();
- throw new SystemException(ex.toString());
- }
- }
+ if (status == ActionStatus.RUNNING)
+ {
+ // check that all ParticipantCompletion participants have completed
+ // throwing a wobbly if not
+
+ ensureNotActive(false);
+
+ doComplete();
+
}
else
- throw new WrongStateException();
- }
+ {
+ throw new WrongStateException();
+ }
+ }
+ /**
+ * invoke the prepare operation in order to drive all CoordinatorCompletion participants to completion
+ * @throws SystemException if any of the participants fails to complete
+ */
+ private void doComplete() throws SystemException
+ {
+ // we can emerge from this with outcome PREPARE_NOTOK if one of the CoordinatorCompletion participants
+ // failed to respond to a completed request. in that case the action status will be PREPARING
+ int outcome = super.prepare(true);
+
+ if (outcome == TwoPhaseOutcome.PREPARE_NOTOK) {
+ if (wscfLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ wscfLogger.arjLoggerI18N.warn("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_4", new Object[] { get_uid() });
+ }
+
+ int heuristicDecision = getHeuristicDecision();
+
+ if (heuristicDecision != TwoPhaseOutcome.PREPARE_OK)
+ {
+ if (wscfLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ wscfLogger.arjLoggerI18N.warn("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_5", new Object[] { TwoPhaseOutcome.stringForm(heuristicDecision) });
+ }
+ }
+
+ if (wscfLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ wscfLogger.arjLoggerI18N.warn("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_6",new Object[] { get_uid() });
+ }
+
+ }
+ }
+
+ /**
+ * if all is well with the TX invoke the phase2Commit operation in order to drive all participants to
+ * closed status, if not invoke Abort.
+ * @return the outcome of the commit or abort
+ * @throws SystemException if there are _any_ uncompleted participants, either because the client has
+ * not called close to close CoordinatorCompletion participants or because not all ParticipantCompletion
+ * participants have notified completion.
+ */
+ public int close () throws SystemException
+ {
+ int outcome;
+
+ if (parent() != null)
+ parent().removeChildAction(this);
+
+ if (status() == ActionStatus.RUNNING) {
+ // check that _all_ participants have completed throwing a SystemException if not
+
+ ensureNotActive(true);
+
+ // ok, we do a complete anyway to ensure we log the participant list and progress to state
+ // PREPARED. this should not fail since the participants ought to just say yeah yeah whateva
+
+ doComplete();
+ }
+
+ switch (status()) {
+ case ActionStatus.PREPARING:
+ {
+ // we failed during prepare so we have to do a phase2Abort in order to clean up
+ // this means we may end up leaving a log entry for an aborted transaction with
+ // a heuristic outcome
+ super.phase2Abort(true);
+ outcome = status();
+ }
+ break;
+ // we managed to prepare so we need to do a phase2Commit
+ case ActionStatus.PREPARED:
+ case ActionStatus.COMMITTING:
+ {
+ if (parent() != null)
+ parent().removeChildAction(this);
+
+ super.phase2Commit(true);
+ // remap the status if there are heuristics
+ outcome = status();
+ int heuristicDecision = getHeuristicDecision();
+
+ switch (heuristicDecision)
+ {
+ case TwoPhaseOutcome.PREPARE_OK:
+ case TwoPhaseOutcome.FINISH_OK:
+ break;
+ case TwoPhaseOutcome.HEURISTIC_ROLLBACK:
+ outcome = ActionStatus.H_ROLLBACK;
+ case TwoPhaseOutcome.HEURISTIC_COMMIT:
+ outcome = ActionStatus.H_COMMIT;
+ case TwoPhaseOutcome.HEURISTIC_MIXED:
+ outcome = ActionStatus.H_MIXED;
+ case TwoPhaseOutcome.HEURISTIC_HAZARD:
+ default:
+ outcome = ActionStatus.H_HAZARD;
+ }
+ }
+ break;
+ // this covers the case where the tx is marked as ABORT_ONLY
+ default:
+ {
+ if (parent() != null)
+ parent().removeChildAction(this);
+
+ // no heuristics to deal with
+
+ outcome = super.Abort();
+ }
+ }
+
+ return outcome;
+ }
+
+ /**
+ * invoke Abort.
+ * @return the outcome of the abort
+ */
+ public int cancel ()
+ {
+ // we cannot do this as per TwoPhaseCoordinator because TxControl and TxStats are package private
+ // but note that JTS does not count this either
+ //if (TxControl.enableStatistics)
+ // TxStats.incrementApplicationRollbacks();
+
+ if (parent() != null)
+ parent().removeChildAction(this);
+
+ int outcome;
+
+ switch (status()) {
+
+ // PREPARING/PREPARED/COMMITTING mean we have committed or tried to commit in which case we
+ // may have entries in the pending and prepared lists and we may have written a log record
+ // which we need to update. it also means we can end up leaving a log entry for an aborted
+ // transaction with a heuristic outcome
+
+ case ActionStatus.PREPARING:
+ case ActionStatus.PREPARED:
+ case ActionStatus.COMMITTING:
+ {
+ super.phase2Abort(true);
+ outcome = status();
+ }
+ break;
+ // RUNNING or ABORT_ONLY means we never got to commit so we can just do a normal abort
+ case ActionStatus.RUNNING:
+ case ActionStatus.ABORT_ONLY:
+ {
+ outcome = super.Abort();
+ }
+ break;
+ // nothing to do if we are in any other state
+ default:
+ {
+ outcome = status();
+ }
+ break;
+ }
+
+ return outcome;
+ }
+
/**
* Enrol the specified participant with the coordinator associated with the
* current thread.
*
- * @param Participant
- * act The participant.
- *
* @exception WrongStateException
* Thrown if the coordinator is not in a state that allows
* participants to be enrolled.
@@ -197,13 +388,13 @@
* Thrown if any other error occurs.
*/
- public void enlistParticipant (Participant act) throws WrongStateException,
+ public void enlistParticipant (RecoverableParticipant act) throws WrongStateException,
DuplicateParticipantException, InvalidParticipantException,
SystemException
{
if (act == null) throw new InvalidParticipantException();
- AbstractRecord rec = new ParticipantRecord(act, new Uid());
+ AbstractRecord rec = new ParticipantRecord((RecoverableParticipant)act, new Uid());
if (add(rec) != AddOutcome.AR_ADDED) throw new WrongStateException();
else
@@ -216,15 +407,14 @@
}
/**
- * Remove the specified participant from the coordinator's list.
+ * 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 (e.g., in a two-phase protocol
- * the coordinator is committing.)
+ * participant to be removed
* @exception SystemException
* Thrown if any other error occurs.
*/
@@ -238,11 +428,19 @@
wscfLogger.log_mesg
.getString("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_2"));
- if (status() == ActionStatus.RUNNING) changeParticipantStatus(
- participantId, DELISTED);
- else
- throw new WrongStateException();
- }
+ 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.log_mesg
+ .getString("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_3"));
+ }
+ }
public synchronized void participantCompleted (String participantId)
throws InvalidParticipantException, WrongStateException,
@@ -253,10 +451,18 @@
wscfLogger.log_mesg
.getString("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_2"));
- if (status() == ActionStatus.RUNNING) changeParticipantStatus(
- participantId, COMPLETED);
- else
- throw new WrongStateException();
+ int status = status();
+ // completed is only legitimate when the TX is in these states
+ switch (status) {
+ case ActionStatus.RUNNING:
+ case ActionStatus.ABORT_ONLY:
+ changeParticipantStatus(participantId, COMPLETED);
+ break;
+ default:
+ throw new WrongStateException(
+ wscfLogger.log_mesg
+ .getString("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_3"));
+ }
}
public synchronized void participantFaulted (String participantId)
@@ -267,18 +473,53 @@
wscfLogger.log_mesg
.getString("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_2"));
- if (status() == ActionStatus.RUNNING)
- {
- // participant faulted means it has compensated and gone away
-
- changeParticipantStatus(participantId, DELISTED);
- }
- else
- throw new SystemException(
+ 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.log_mesg
.getString("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_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.log_mesg
+ .getString("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_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.log_mesg
+ .getString("com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator_3"));
+ }
+ }
+
/**
* @exception SystemException
* Thrown if any error occurs.
@@ -304,6 +545,11 @@
return _theId;
}
+ public String type ()
+ {
+ return "/StateManager/BasicAction/AtomicAction/Sagas/ACCoordinator";
+ }
+
private final void changeParticipantStatus (String participantId, int status)
throws InvalidParticipantException, SystemException
{
@@ -333,10 +579,14 @@
{
found = true;
- if (status == DELISTED) pr.delist();
- else
+ if (status == DELISTED) {
+ pr.delist(false);
+ } else if (status == FAILED) {
+ pr.delist(true);
+ } else {
pr.completed();
- }
+ }
+ }
}
absRec = iter.iterate();
Modified: labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/CoordinatorControl.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/CoordinatorControl.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/CoordinatorControl.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -46,6 +46,7 @@
import com.arjuna.mw.wscf.common.CoordinatorId;
import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.coordinator.BasicAction;
import com.arjuna.mw.wsas.UserActivityFactory;
@@ -114,7 +115,7 @@
/**
* The current activity is completing with the specified completion status.
*
- * @param CompletionStatus cs The completion status to use.
+ * @param cs The completion status to use.
*
* @return The result of terminating the relationship of this HLS and
* the current activity.
@@ -128,14 +129,12 @@
if ((cs != null) && (cs instanceof Success))
{
// commit
-
- outcome = current.end(true);
+ outcome = current.close();
}
else
{
// abort
-
- outcome = current.cancel();
+ outcome = current.cancel();
}
_coordinators.remove(currentActivity());
@@ -316,7 +315,7 @@
* @exception SystemException Thrown if any other error occurs.
*/
- public void enlistParticipant (Participant act) throws WrongStateException, DuplicateParticipantException, InvalidParticipantException, NoCoordinatorException, SystemException
+ public void enlistParticipant (RecoverableParticipant act) throws WrongStateException, DuplicateParticipantException, InvalidParticipantException, NoCoordinatorException, SystemException
{
currentCoordinator().enlistParticipant(act);
}
@@ -346,7 +345,12 @@
{
currentCoordinator().participantFaulted(participantId);
}
-
+
+ public void participantCannotComplete (String participantId) throws NoActivityException, InvalidParticipantException, WrongStateException, SystemException
+ {
+ currentCoordinator().participantCannotComplete(participantId);
+ }
+
public final ACCoordinator currentCoordinator () throws NoCoordinatorException, SystemException
{
ACCoordinator coord = (ACCoordinator) _coordinators.get(currentActivity());
Modified: labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/CoordinatorServiceImple.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/CoordinatorServiceImple.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/CoordinatorServiceImple.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -106,7 +106,7 @@
* Start a new activity. If there is already an activity associated
* with the thread then it will be nested.
*
- * @param int timeout The timeout associated with the activity. If the
+ * @param timeout The timeout associated with the activity. If the
* activity has not been terminated by the time this period elapses, then
* it will automatically be terminated.
* @exception WrongStateException Thrown if the currently associated
@@ -313,7 +313,7 @@
* each thread and this means that no application specified timeout is
* set for activities.
*
- * @param int timeout The timeout (in seconds) to associate with all
+ * @param timeout The timeout (in seconds) to associate with all
* subsequently created activities. This value must be 0 or greater.
*
* @exception InvalidTimeoutException Thrown if the timeout value provided
@@ -369,7 +369,7 @@
* activities that it may already be associated with. If the parameter is
* null then the thread is associated with no activity.
*
- * @param ActivityHierarchy tx The activity to associate with this thread. This
+ * @param tx The activity to associate with this thread. This
* may be null in which case the current thread becomes associated with
* no activity.
*
@@ -387,7 +387,7 @@
* Enrol the specified participant with the coordinator associated with
* the current thread.
*
- * @param Participant act The participant.
+ * @param act The participant.
*
* @exception WrongStateException Thrown if the coordinator is not in a
* state that allows participants to be enrolled.
@@ -398,7 +398,7 @@
* @exception SystemException Thrown if any other error occurs.
*/
- public void enlistParticipant (Participant act) throws WrongStateException, DuplicateParticipantException, InvalidParticipantException, NoCoordinatorException, SystemException
+ public void enlistParticipant (RecoverableParticipant act) throws WrongStateException, DuplicateParticipantException, InvalidParticipantException, NoCoordinatorException, SystemException
{
_coordManager.enlistParticipant(act);
}
@@ -438,6 +438,20 @@
}
}
+ public void participantCannotComplete (String participantId) throws NoActivityException, InvalidParticipantException, WrongStateException, SystemException
+ {
+ _coordManager.participantCannotComplete(participantId);
+
+ try
+ {
+ setCancelOnly();
+ }
+ catch (Exception ex)
+ {
+ throw new SystemException(ex.toString());
+ }
+ }
+
/**
* @return the token representing the current activity context hierarchy,
* or null if there is none associated with the invoking thread.
Modified: labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/ParticipantRecord.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/ParticipantRecord.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/sagas/arjunacore/ParticipantRecord.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -41,10 +41,14 @@
import com.arjuna.mw.wscf.model.sagas.participants.Participant;
import com.arjuna.mw.wscf.model.sagas.participants.ParticipantWithComplete;
+import com.arjuna.mw.wscf.model.sagas.participants.RecoverableParticipant;
+import com.arjuna.mw.wscf.model.sagas.exceptions.CompensateFailedException;
+import com.arjuna.mw.wscf.model.sagas.exceptions.CancelFailedException;
import com.arjuna.mw.wsas.exceptions.*;
import com.arjuna.mw.wscf.exceptions.*;
+import com.arjuna.webservices.util.ClassLoaderHelper;
import java.io.PrintWriter;
@@ -70,7 +74,7 @@
* ParticipantRecord {0} - null participant provided!
*/
- public ParticipantRecord (Participant theResource, Uid id)
+ public ParticipantRecord (RecoverableParticipant theResource, Uid id)
{
super(id, null, ObjectType.ANDPERSISTENT);
@@ -109,7 +113,7 @@
{
// TODO add to record list
- return RecordType.USER_DEF_FIRST1;
+ return RecordType.XTS_WSBA_RECORD;
}
/**
@@ -268,15 +272,39 @@
{
return TwoPhaseOutcome.FINISH_ERROR;
}
- catch (WrongStateException ex)
- {
- return TwoPhaseOutcome.FINISH_ERROR;
- }
+ catch (WrongStateException ex)
+ {
+ // this indicates a fail occured and was detected during cancel (or compensation?) so we return a
+ // HEURISTIC_HAZARD which will place the participant in the heuristic list
+ return TwoPhaseOutcome.HEURISTIC_HAZARD;
+ }
+ catch (CancelFailedException ex)
+ {
+ // this indicates a fail occured and was detected during cancel so we return a HEURISTIC_HAZARD
+ // which will place the participant in the heuristic list
+ return TwoPhaseOutcome.HEURISTIC_HAZARD;
+ }
+ catch (CompensateFailedException ex)
+ {
+ // this indicates a fail occured during compensation so we return a HEURISTIC_HAZARD
+ // which will place the participant in the heuristic list
+ return TwoPhaseOutcome.HEURISTIC_HAZARD;
+ }
catch (SystemException ex)
{
- return TwoPhaseOutcome.HEURISTIC_HAZARD;
+ // this indicates a comms failure so we return FINISH_ERROR which will place
+ // the participant in the failed list and cause a retry of the close
+ return TwoPhaseOutcome.FINISH_ERROR;
}
+ // we are not guaranteed to detect all state transitions so we still have to
+ // make sure we did not fail and then end while we were trying to cancel or
+ // compensate
+
+ if (_failed) {
+ return TwoPhaseOutcome.HEURISTIC_HAZARD;
+ }
+
return TwoPhaseOutcome.FINISH_OK;
}
else
@@ -319,14 +347,24 @@
}
catch (WrongStateException ex)
{
- return TwoPhaseOutcome.NOT_PREPARED;
+ // this indicates a failure to close so we notify a heuristic hazard
+ return TwoPhaseOutcome.HEURISTIC_HAZARD;
}
- catch (SystemException ex)
+ catch (SystemException ex)
{
- return TwoPhaseOutcome.HEURISTIC_HAZARD;
+ // this indicates a comms failure so we return FINISH_ERROR which will place
+ // the participant in the failed list and cause a retry of the close
+ return TwoPhaseOutcome.FINISH_ERROR;
}
- return TwoPhaseOutcome.FINISH_OK;
+ // if we have failed we notify a heuristic hazard to ensure that the
+ // participant is placed in the heuristic list and the transaction is logged
+
+ if (_failed) {
+ return TwoPhaseOutcome.HEURISTIC_HAZARD;
+ }
+
+ return TwoPhaseOutcome.FINISH_OK;
}
else
return TwoPhaseOutcome.FINISH_ERROR;
@@ -356,13 +394,24 @@
{
try
{
- if (_resourceHandle != null)
- {
- return TwoPhaseOutcome.PREPARE_OK;
- }
- else
- return TwoPhaseOutcome.PREPARE_NOTOK;
- }
+ boolean result;
+ // only complete if we have not exited
+ if (!_exited) {
+ result = complete();
+ } else {
+ result = false;
+ }
+ // if we have failed we return heuristic hazard so the participant is added to
+ // the heuristic list and the transaction is logged
+
+ if (_failed) {
+ return TwoPhaseOutcome.HEURISTIC_HAZARD;
+ } else if (_exited) {
+ return TwoPhaseOutcome.PREPARE_READONLY;
+ } else {
+ return (result ? TwoPhaseOutcome.PREPARE_OK: TwoPhaseOutcome.PREPARE_NOTOK);
+ }
+ }
catch (Exception e6)
{
wscfLogger.arjLoggerI18N
@@ -426,7 +475,7 @@
{
try
{
- if (!_exited) _resourceHandle.close();
+ if (!_exited) _resourceHandle.close();
}
catch (InvalidParticipantException ex)
{
@@ -434,14 +483,25 @@
}
catch (WrongStateException ex)
{
- return TwoPhaseOutcome.FINISH_ERROR;
+ // this indicates a failure to close so we notify a heuristic hazard
+ return TwoPhaseOutcome.HEURISTIC_HAZARD;
}
catch (SystemException ex)
{
- return TwoPhaseOutcome.HEURISTIC_HAZARD;
+ // this indicates a comms failure so we return FINISH_ERROR which will place
+ // the participant in the failed list and cause a retry of the close
+ return TwoPhaseOutcome.FINISH_ERROR;
}
- return TwoPhaseOutcome.FINISH_OK;
+ // if we have failed we notify a heuristic hazard to ensure that the
+ // participant is placed in the heuristic list and the transaction is logged
+
+ if (_failed) {
+ return TwoPhaseOutcome.HEURISTIC_HAZARD;
+ }
+ // if we closed or we exited then all is ok
+
+ return TwoPhaseOutcome.FINISH_OK;
}
else
return TwoPhaseOutcome.FINISH_ERROR;
@@ -517,7 +577,7 @@
return false;
}
- /**
+ /**
* @message com.arjuna.mwlabs.wscf.model.sagas.coordinator.arjunacore.ParticipantRecord_13
* [com.arjuna.mwlabs.wscf.model.sagas.coordinator.arjunacore.ParticipantRecord_13] -
* ParticipantRecord.complete {0} caught: {1}
@@ -533,13 +593,13 @@
{
try
{
- if (!_completed)
+ if (isActive())
{
if (_resourceHandle instanceof ParticipantWithComplete)
{
((ParticipantWithComplete) _resourceHandle)
.complete();
- _completed = true;
+ completed();
}
result = true;
@@ -607,14 +667,19 @@
{
try
{
- // TODO
+ String resourcehandleImplClassName = os.unpackString();
+ Class clazz = ClassLoaderHelper.forName(ParticipantRecord.class, resourcehandleImplClassName);
+ _resourceHandle = (RecoverableParticipant)clazz.newInstance();
- if (_resourceHandle.restore_state(os))
+ result = _resourceHandle.restore_state(os);
+
+ if (result) {
_timeout = os.unpackLong();
-
- /*
- * TODO: unpack qualifiers and coord id.
- */
+ _exited = os.unpackBoolean();
+ if (_exited) {
+ _failed = os.unpackBoolean();
+ }
+ }
}
catch (Exception ex)
{
@@ -644,12 +709,17 @@
{
try
{
- // TODO
-
- if (_resourceHandle.save_state(os))
+ os.packString(_resourceHandle.getClass().getName()); // TODO: a shorter value whould be more efficient.
+ result = _resourceHandle.save_state(os);
+ if (result) {
os.packLong(_timeout);
+ os.packBoolean(_exited);
+ if (_exited) {
+ os.packBoolean(_failed);
+ }
+ }
- /*
+ /*
* TODO: pack qualifiers and coord id.
*/
}
@@ -667,7 +737,27 @@
return result;
}
- public String type ()
+ /**
+ * called during recovery activation to propagate the coordinator to the underlying stubs and their protocol
+ * engines allowing the engines to establish a back channel to the cooridnator for flow of remote participant
+ * requests.
+ */
+
+ public void setRecoveryCoordinator(ACCoordinator coordinator)
+ {
+ try {
+ // try to propagate setCoordinator to the resource
+
+ RecoverableParticipant recoverableParticipant = (RecoverableParticipant) _resourceHandle;
+ recoverableParticipant.setCoordinator(coordinator);
+ } catch (ClassCastException e) {
+ // ignore as this is obviously not an instance of RecoverableParticipant
+ // it must be a participant registered via the local API which means there is
+ // no BAParticipantManager instance around to drive messages to the coordinator
+ }
+ }
+
+ public String type ()
{
return "/StateManager/AbstractRecord/WSCF/ArjunaCore/ParticipantRecord";
}
@@ -675,11 +765,12 @@
public boolean doSave ()
{
/*
- * If the participant has exited, then we don't need to save anything
- * about it in the transaction log.
+ * If the participant has exited without failure, then we don't need to save anything
+ * about it in the transaction log. If it has not exited or it has exited with failure
+ * we do need to log it
*/
- return !_exited;
+ return (!_exited || _failed);
}
public void merge (AbstractRecord a)
@@ -710,16 +801,46 @@
return false;
}
- public final void delist ()
+ /**
+ * record the fact that this participant has exited
+ *
+ * @param failed true if the exit was because of a failure i.e. the participant may be in an unclean state
+ */
+ public final void delist (boolean failed)
{
_exited = true;
- }
+ _failed = failed;
+ }
- public final void completed ()
- {
- _completed = true;
- }
+ /**
+ * record the fact that this participant has completed
+ */
+ public final synchronized void completed ()
+ {
+ _completed = true;
+ }
+ /**
+ * is the participant is still able to be sent a complete request
+ *
+ * @caveat it is only appropriate to call this if this is a CoordinatorCompletion participant
+ * @return true if the participant is still able to be sent a complete request otherwise false
+ */
+ public final synchronized boolean isActive ()
+ {
+ return !_completed && !_exited;
+ }
+
+ /**
+ * is this a ParticipantCompletion participant
+ * @return true if this is a ParticipantCompletion participant otherwise false
+ */
+ public final boolean isParticipantCompletion ()
+ {
+ // n.b. this is ok if _resourceHandle is null
+ return (_resourceHandle instanceof ParticipantWithComplete);
+ }
+
/*
* Protected constructor used by crash recovery.
*/
@@ -733,14 +854,16 @@
_coordId = null;
}
- private Participant _resourceHandle;
+ private RecoverableParticipant _resourceHandle;
private long _timeout;
private CoordinatorIdImple _coordId;
- private boolean _exited = false;
+ private boolean _exited = false;
+ private boolean _failed = false;
+
private boolean _completed = false;
}
Modified: labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/twophase/arjunacore/ACCoordinator.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/twophase/arjunacore/ACCoordinator.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/twophase/arjunacore/ACCoordinator.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -101,10 +101,6 @@
* This implementation only supports coordination at the end of the
* activity.
*
- * @param CompletionStatus
- * cs The completion status to use when determining how to
- * execute the protocol.
- *
* @exception WrongStateException
* Thrown if the coordinator is in a state the does not allow
* coordination to occur.
@@ -127,9 +123,8 @@
* Enrol the specified participant with the coordinator associated with the
* current thread.
*
- * @param Participant
- * act The participant.
- *
+ * @param act The participant.
+ *
* @exception WrongStateException
* Thrown if the coordinator is not in a state that allows
* participants to be enrolled.
@@ -185,8 +180,7 @@
* Enrol the specified synchronization with the coordinator associated with
* the current thread.
*
- * @param Synchronization
- * act The synchronization to add.
+ * @param act The synchronization to add.
*
* @exception WrongStateException
* Thrown if the coordinator is not in a state that allows
Modified: labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/twophase/arjunacore/ParticipantRecord.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/twophase/arjunacore/ParticipantRecord.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSCF/classes/com/arjuna/mwlabs/wscf/model/twophase/arjunacore/ParticipantRecord.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -109,7 +109,7 @@
{
// TODO add specific record type.
- return RecordType.USER_DEF_FIRST0;
+ return RecordType.XTS_WSAT_RECORD;
}
/**
Modified: labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/RegistrarImple.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/RegistrarImple.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/RegistrarImple.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -83,8 +83,6 @@
* mappers or to the same register mapper with different protocol
* identifiers.
*
- * @param serviceAddress
- * the address of the service
* @param protocolIdentifier
* the protocol identifier
*/
@@ -96,8 +94,6 @@
/**
* Registers the interest of participant in a particular protocol.
*
- * @param participantProtocolServiceAddress
- * the address of the participant protocol service
* @param protocolIdentifier
* the protocol identifier
*
@@ -228,8 +224,6 @@
* register mappers or from the same register mapper with different protocol
* identifiers.
*
- * @param serviceAddress
- * the address of the service
* @param protocolIdentifier
* the protocol identifier
*/
Modified: labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/local/LocalRegistrarImple.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/local/LocalRegistrarImple.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/local/LocalRegistrarImple.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -41,6 +41,8 @@
import com.arjuna.wsc.NoActivityException;
import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
+import com.arjuna.wst.stub.LocalParticipantCompletionParticipantStub;
+import com.arjuna.wst.stub.LocalCoordinatorCompletionParticipantStub;
/**
* This class simulates the use of the real RegistrarImple, which we can't
@@ -94,7 +96,8 @@
try
{
- _coordManager.enlistParticipant(new com.arjuna.mwlabs.wst.ba.participants.BusinessAgreementWithParticipantCompletionImple((BusinessAgreementWithParticipantCompletionParticipant) participant, participantId));
+ LocalParticipantCompletionParticipantStub recoverableParticipant = new LocalParticipantCompletionParticipantStub((BusinessAgreementWithParticipantCompletionParticipant)participant, participantId);
+ _coordManager.enlistParticipant(new com.arjuna.mwlabs.wst.ba.participants.BusinessAgreementWithParticipantCompletionImple(recoverableParticipant, participantId));
}
catch (Exception ex)
{
@@ -107,7 +110,8 @@
{
try
{
- _coordManager.enlistParticipant(new com.arjuna.mwlabs.wst.ba.participants.BusinessAgreementWithCoordinatorCompletionImple((BusinessAgreementWithCoordinatorCompletionParticipant) participant, participantId));
+ LocalCoordinatorCompletionParticipantStub recoverableParticipant = new LocalCoordinatorCompletionParticipantStub((BusinessAgreementWithCoordinatorCompletionParticipant)participant, participantId);
+ _coordManager.enlistParticipant(new com.arjuna.mwlabs.wst.ba.participants.BusinessAgreementWithCoordinatorCompletionImple(recoverableParticipant, participantId));
}
catch (Exception ex)
{
Modified: labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/participants/BusinessAgreementWithCoordinatorCompletionImple.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/participants/BusinessAgreementWithCoordinatorCompletionImple.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/participants/BusinessAgreementWithCoordinatorCompletionImple.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -35,6 +35,7 @@
import com.arjuna.mw.wsas.exceptions.WrongStateException;
import com.arjuna.mw.wscf.exceptions.InvalidParticipantException;
import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
+import com.arjuna.wst.RecoverableBusinessAgreementWithCoordinatorCompletionParticipant;
/**
* @author Mark Little (mark.little at arjuna.com)
@@ -45,16 +46,21 @@
public class BusinessAgreementWithCoordinatorCompletionImple extends
BusinessAgreementWithParticipantCompletionImple implements
- com.arjuna.mw.wscf.model.sagas.participants.ParticipantWithComplete
+ com.arjuna.mw.wscf.model.sagas.participants.RecoverableParticipantWithComplete
{
public BusinessAgreementWithCoordinatorCompletionImple (
- BusinessAgreementWithCoordinatorCompletionParticipant participant,
+ RecoverableBusinessAgreementWithCoordinatorCompletionParticipant participant,
String identifier)
{
super(participant, identifier);
}
+ public BusinessAgreementWithCoordinatorCompletionImple ()
+ {
+ super();
+ }
+
public void complete () throws InvalidParticipantException,
WrongStateException, SystemException
{
Modified: labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/participants/BusinessAgreementWithParticipantCompletionImple.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/participants/BusinessAgreementWithParticipantCompletionImple.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/participants/BusinessAgreementWithParticipantCompletionImple.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -40,7 +40,10 @@
import com.arjuna.mw.wscf.exceptions.InvalidParticipantException;
import com.arjuna.mw.wscf.model.sagas.exceptions.CompensateFailedException;
import com.arjuna.mwlabs.wst.util.PersistableParticipantHelper;
+import com.arjuna.mwlabs.wst.ba.remote.BARecoveryParticipantManagerImple;
+import com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator;
import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
+import com.arjuna.wst.RecoverableBusinessAgreementWithParticipantCompletionParticipant;
// TODO crash recovery (for EVERYTHING!!)
@@ -54,11 +57,11 @@
*/
public class BusinessAgreementWithParticipantCompletionImple implements
- com.arjuna.mw.wscf.model.sagas.participants.Participant
+ com.arjuna.mw.wscf.model.sagas.participants.RecoverableParticipant
{
//
public BusinessAgreementWithParticipantCompletionImple (
- BusinessAgreementWithParticipantCompletionParticipant resource,
+ RecoverableBusinessAgreementWithParticipantCompletionParticipant resource,
String id)
{
_resource = resource;
@@ -67,6 +70,13 @@
id);
}
+ public BusinessAgreementWithParticipantCompletionImple ()
+ {
+ _resource = null;
+ _identifier = null;
+ _baParticipantManager = null;
+ }
+
public void close () throws InvalidParticipantException,
WrongStateException, SystemException
{
@@ -101,14 +111,19 @@
else
throw new InvalidParticipantException();
}
+ catch (com.arjuna.wst.FaultedException ex)
+ {
+ // n.b. in 1.0 we will never see this
+ throw new SystemException(ex.toString());
+ }
catch (com.arjuna.wst.WrongStateException ex)
{
throw new WrongStateException(ex.toString());
}
- catch (com.arjuna.wst.SystemException ex)
- {
- throw new SystemException(ex.toString());
- }
+ catch (com.arjuna.wst.SystemException ex)
+ {
+ throw new SystemException(ex.toString());
+ }
}
public void compensate () throws CompensateFailedException,
@@ -211,7 +226,7 @@
final Object resource = PersistableParticipantHelper.restore_state(is) ;
if (resource != null)
{
- _resource = (BusinessAgreementWithParticipantCompletionParticipant)resource ;
+ _resource = (RecoverableBusinessAgreementWithParticipantCompletionParticipant)resource ;
return true ;
}
else
@@ -220,8 +235,20 @@
}
}
- protected BusinessAgreementWithParticipantCompletionParticipant _resource;
+ /**
+ * establish a back channel from the underlying stub to the coordinator by creating a participant manager which
+ * will forward messages to the coordinator. this is only called during recovery processing
+ *
+ * @param coordinator
+ */
+ public void setCoordinator(ACCoordinator coordinator)
+ {
+ _baParticipantManager = new BARecoveryParticipantManagerImple(coordinator, _identifier);
+ _resource.setParticipantManager(_baParticipantManager);
+ }
+ protected RecoverableBusinessAgreementWithParticipantCompletionParticipant _resource;
+
private String _identifier = null;
private com.arjuna.wst.BAParticipantManager _baParticipantManager = null;
Added: labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/remote/BARecoveryParticipantManagerImple.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/remote/BARecoveryParticipantManagerImple.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WSTX/classes10/com/arjuna/mwlabs/wst/ba/remote/BARecoveryParticipantManagerImple.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,143 @@
+/*
+ * 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) 2003,
+ *
+ * Arjuna Technologies Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: BAParticipantManagerImple.java,v 1.5.6.1 2005/11/22 10:36:08 kconner Exp $
+ */
+
+package com.arjuna.mwlabs.wst.ba.remote;
+
+import com.arjuna.wst.SystemException;
+import com.arjuna.wst.UnknownTransactionException;
+import com.arjuna.wst.WrongStateException;
+import com.arjuna.wst.BAParticipantManager;
+import com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator;
+
+import javax.xml.namespace.QName;
+
+/**
+ * This is the interface that the core exposes in order to allow different
+ * types of participants to be enrolled. The messaging layer continues to
+ * work in terms of the registrar, but internally we map to one of these
+ * methods.
+ *
+ * This could also be the interface that high-level users see (e.g., at the
+ * application Web Service).
+ */
+
+public class BARecoveryParticipantManagerImple implements BAParticipantManager
+{
+
+ public BARecoveryParticipantManagerImple(ACCoordinator coordinator, String participantId)
+ {
+ this.coordinator = coordinator;
+ this.participantId = participantId;
+ }
+
+ public void exit () throws WrongStateException, UnknownTransactionException, SystemException
+ {
+ try
+ {
+ coordinator.delistParticipant(participantId);
+ }
+ catch (com.arjuna.mw.wscf.exceptions.InvalidParticipantException ex)
+ {
+ throw new SystemException("UnknownParticipantException");
+ }
+ catch (com.arjuna.mw.wsas.exceptions.WrongStateException ex)
+ {
+ throw new WrongStateException();
+ }
+ catch (com.arjuna.mw.wsas.exceptions.SystemException ex)
+ {
+ throw new SystemException(ex.toString());
+ }
+ }
+
+ public void completed () throws WrongStateException, UnknownTransactionException, SystemException
+ {
+ try
+ {
+ coordinator.participantCompleted(participantId);
+ }
+ catch (com.arjuna.mw.wscf.exceptions.InvalidParticipantException ex)
+ {
+ throw new SystemException("UnknownParticipantException");
+ }
+ catch (com.arjuna.mw.wsas.exceptions.WrongStateException ex)
+ {
+ throw new WrongStateException();
+ }
+ catch (com.arjuna.mw.wsas.exceptions.SystemException ex)
+ {
+ throw new SystemException(ex.toString());
+ }
+ }
+
+ public void fault () throws SystemException
+ {
+ try
+ {
+ coordinator.participantFaulted(participantId);
+ }
+ catch (com.arjuna.mw.wscf.exceptions.InvalidParticipantException ex)
+ {
+ throw new SystemException("UnknownParticipantException");
+ }
+ catch (com.arjuna.mw.wsas.exceptions.SystemException ex)
+ {
+ throw new SystemException(ex.toString());
+ }
+ }
+
+ /**
+ * An unknown error has occurred that the participant wants to communicate
+ * to the coordinator.
+ */
+
+ public void unknown() throws SystemException
+ {
+ /*
+ * this API is not needed here -- quite probably it is not needed in the non recovery case either
+ */
+ }
+
+ /**
+ * An error has occurred during the execution of the protocol that the
+ * participant wants to communicate to the coordinator.
+ */
+ public void error () throws SystemException
+ {
+ /*
+ * this API is not needed here -- quite probably it is not needed in the non recovery case either
+ */
+ }
+
+ private ACCoordinator coordinator;
+ private String participantId;
+
+}
\ No newline at end of file
Modified: labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/participants/BusinessAgreementWithCoordinatorCompletionImple.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/participants/BusinessAgreementWithCoordinatorCompletionImple.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/participants/BusinessAgreementWithCoordinatorCompletionImple.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -36,6 +36,7 @@
import com.arjuna.mw.wscf.exceptions.InvalidParticipantException;
import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
import com.arjuna.mwlabs.wst11.ba.participants.BusinessAgreementWithParticipantCompletionImple;
+import com.arjuna.wst11.RecoverableBusinessAgreementWithCoordinatorCompletionParticipant;
/**
* @author Mark Little (mark.little at arjuna.com)
@@ -46,16 +47,21 @@
public class BusinessAgreementWithCoordinatorCompletionImple extends
BusinessAgreementWithParticipantCompletionImple implements
- com.arjuna.mw.wscf.model.sagas.participants.ParticipantWithComplete
+ com.arjuna.mw.wscf.model.sagas.participants.RecoverableParticipantWithComplete
{
public BusinessAgreementWithCoordinatorCompletionImple(
- BusinessAgreementWithCoordinatorCompletionParticipant participant,
+ RecoverableBusinessAgreementWithCoordinatorCompletionParticipant participant,
String identifier)
{
super(participant, identifier);
}
+ public BusinessAgreementWithCoordinatorCompletionImple()
+ {
+ super();
+ }
+
public void complete () throws InvalidParticipantException,
WrongStateException, SystemException
{
Modified: labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/participants/BusinessAgreementWithParticipantCompletionImple.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/participants/BusinessAgreementWithParticipantCompletionImple.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/participants/BusinessAgreementWithParticipantCompletionImple.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -39,11 +39,20 @@
import com.arjuna.mw.wsas.exceptions.WrongStateException;
import com.arjuna.mw.wscf.exceptions.InvalidParticipantException;
import com.arjuna.mw.wscf.model.sagas.exceptions.CompensateFailedException;
+import com.arjuna.mw.wscf.model.sagas.exceptions.CancelFailedException;
import com.arjuna.mwlabs.wst.util.PersistableParticipantHelper;
import com.arjuna.mwlabs.wst11.ba.remote.BAParticipantManagerImple;
+import com.arjuna.mwlabs.wst11.ba.remote.BARecoveryParticipantManagerImple;
+import com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator;
import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
+import com.arjuna.wst.stub.SystemCommunicationException;
import com.arjuna.wst11.BAParticipantManager;
+import com.arjuna.wst11.RecoverableBusinessAgreementWithParticipantCompletionParticipant;
+import com.arjuna.wst11.messaging.engines.ParticipantCompletionCoordinatorEngine;
+import com.arjuna.wst11.stub.BusinessAgreementWithParticipantCompletionStub;
+import java.io.IOException;
+
// TODO crash recovery (for EVERYTHING!!)
// TODO re-architect!!
@@ -56,18 +65,25 @@
*/
public class BusinessAgreementWithParticipantCompletionImple implements
- com.arjuna.mw.wscf.model.sagas.participants.Participant
+ com.arjuna.mw.wscf.model.sagas.participants.RecoverableParticipant
{
//
- public BusinessAgreementWithParticipantCompletionImple(
- BusinessAgreementWithParticipantCompletionParticipant resource,
- String id)
- {
- _resource = resource;
- _identifier = id;
- _baParticipantManager = new BAParticipantManagerImple(id);
- }
+public BusinessAgreementWithParticipantCompletionImple(
+ RecoverableBusinessAgreementWithParticipantCompletionParticipant resource,
+ String id)
+{
+ _resource = resource;
+ _identifier = id;
+ _baParticipantManager = new BAParticipantManagerImple(id);
+}
+ public BusinessAgreementWithParticipantCompletionImple()
+ {
+ _resource = null;
+ _identifier = null;
+ _baParticipantManager = null;
+ }
+
public void close () throws InvalidParticipantException,
WrongStateException, SystemException
{
@@ -90,7 +106,7 @@
}
}
- public void cancel () throws InvalidParticipantException,
+ public void cancel () throws CancelFailedException, InvalidParticipantException,
WrongStateException, SystemException
{
try
@@ -106,10 +122,15 @@
{
throw new WrongStateException(ex.toString());
}
- catch (com.arjuna.wst.SystemException ex)
- {
- throw new SystemException(ex.toString());
- }
+ catch (com.arjuna.wst.FaultedException ex)
+ {
+ // we can see this in 1.1
+ throw new CancelFailedException(ex.toString());
+ }
+ catch (com.arjuna.wst.SystemException ex)
+ {
+ throw new SystemException(ex.toString());
+ }
}
public void compensate () throws CompensateFailedException,
@@ -204,15 +225,25 @@
public boolean save_state (OutputObjectState os)
{
+ try {
+ os.packString(_identifier);
+ } catch (IOException e) {
+ return false;
+ }
return PersistableParticipantHelper.save_state(os, _resource) ;
}
public boolean restore_state (InputObjectState is)
{
+ try {
+ _identifier = is.unpackString();
+ } catch (IOException e) {
+ return false;
+ }
final Object resource = PersistableParticipantHelper.restore_state(is) ;
if (resource != null)
{
- _resource = (BusinessAgreementWithParticipantCompletionParticipant)resource ;
+ _resource = (RecoverableBusinessAgreementWithParticipantCompletionParticipant)resource ;
return true ;
}
else
@@ -221,8 +252,20 @@
}
}
- protected BusinessAgreementWithParticipantCompletionParticipant _resource;
+ /**
+ * establish a back channel from the underlying stub to the coordinator by creating a participant manager which
+ * will forward messages to the coordinator. thisis oly called during recovery processing
+ *
+ * @param coordinator
+ */
+ public void setCoordinator(ACCoordinator coordinator)
+ {
+ _baParticipantManager = new BARecoveryParticipantManagerImple(coordinator, _identifier);
+ _resource.setParticipantManager(_baParticipantManager);
+ }
+ protected RecoverableBusinessAgreementWithParticipantCompletionParticipant _resource;
+
private String _identifier = null;
private BAParticipantManager _baParticipantManager = null;
Modified: labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/remote/BAParticipantManagerImple.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/remote/BAParticipantManagerImple.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/remote/BAParticipantManagerImple.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -177,7 +177,41 @@
public void cannotComplete () throws WrongStateException, UnknownTransactionException, SystemException
{
- exit();
+ try
+ {
+ if (_hier == null)
+ throw new UnknownTransactionException();
+
+ _coordManager.resume(_hier);
+
+ _coordManager.participantCannotComplete(_participantId);
+
+ _coordManager.suspend();
+ }
+ catch (final InvalidActivityException iae)
+ {
+ throw new SystemException("UnknownTransactionException");
+ }
+ catch (final UnknownTransactionException ute)
+ {
+ throw new SystemException("UnknownTransactionException");
+ }
+ catch (com.arjuna.mw.wscf.exceptions.InvalidParticipantException ex)
+ {
+ throw new SystemException("UnknownParticipantException");
+ }
+ catch (com.arjuna.mw.wsas.exceptions.WrongStateException ex)
+ {
+ throw new WrongStateException();
+ }
+ catch (com.arjuna.mw.wsas.exceptions.NoActivityException ex)
+ {
+ throw new SystemException("UnknownTransactionException");
+ }
+ catch (com.arjuna.mw.wsas.exceptions.SystemException ex)
+ {
+ throw new SystemException(ex.toString());
+ }
}
public void error () throws SystemException
Added: labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/remote/BARecoveryParticipantManagerImple.java
===================================================================
--- labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/remote/BARecoveryParticipantManagerImple.java (rev 0)
+++ labs/jbosstm/trunk/XTS/WSTX/classes11/com/arjuna/mwlabs/wst11/ba/remote/BARecoveryParticipantManagerImple.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,153 @@
+/*
+ * 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) 2003,
+ *
+ * Arjuna Technologies Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: BAParticipantManagerImple.java,v 1.5.6.1 2005/11/22 10:36:08 kconner Exp $
+ */
+
+package com.arjuna.mwlabs.wst11.ba.remote;
+
+import com.arjuna.mw.wsas.activity.ActivityHierarchy;
+import com.arjuna.mw.wsas.exceptions.InvalidActivityException;
+import com.arjuna.mw.wscf11.model.sagas.CoordinatorManagerFactory;
+import com.arjuna.mw.wscf.model.sagas.api.CoordinatorManager;
+import com.arjuna.wst.SystemException;
+import com.arjuna.wst.UnknownTransactionException;
+import com.arjuna.wst.WrongStateException;
+import com.arjuna.wst11.BAParticipantManager;
+import com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator;
+
+import javax.xml.namespace.QName;
+
+/**
+ * This is the interface that the core exposes in order to allow different
+ * types of participants to be enrolled. The messaging layer continues to
+ * work in terms of the registrar, but internally we map to one of these
+ * methods.
+ *
+ * This could also be the interface that high-level users see (e.g., at the
+ * application Web Service).
+ */
+
+public class BARecoveryParticipantManagerImple implements BAParticipantManager
+{
+
+ public BARecoveryParticipantManagerImple(ACCoordinator coordinator, String participantId)
+ {
+ this.coordinator = coordinator;
+ this.participantId = participantId;
+ }
+
+ public void exit () throws WrongStateException, UnknownTransactionException, SystemException
+ {
+ try
+ {
+ coordinator.delistParticipant(participantId);
+ }
+ catch (com.arjuna.mw.wscf.exceptions.InvalidParticipantException ex)
+ {
+ throw new SystemException("UnknownParticipantException");
+ }
+ catch (com.arjuna.mw.wsas.exceptions.WrongStateException ex)
+ {
+ throw new WrongStateException();
+ }
+ catch (com.arjuna.mw.wsas.exceptions.SystemException ex)
+ {
+ throw new SystemException(ex.toString());
+ }
+ }
+
+ public void completed () throws WrongStateException, UnknownTransactionException, SystemException
+ {
+ try
+ {
+ coordinator.participantCompleted(participantId);
+ }
+ catch (com.arjuna.mw.wscf.exceptions.InvalidParticipantException ex)
+ {
+ throw new SystemException("UnknownParticipantException");
+ }
+ catch (com.arjuna.mw.wsas.exceptions.WrongStateException ex)
+ {
+ throw new WrongStateException();
+ }
+ catch (com.arjuna.mw.wsas.exceptions.SystemException ex)
+ {
+ throw new SystemException(ex.toString());
+ }
+ }
+
+ public void fail (final QName exceptionIdentifier) throws SystemException
+ {
+ try
+ {
+ // fail means faulted as far as the coordinator manager is concerned
+ coordinator.participantFaulted(participantId);
+ }
+ catch (com.arjuna.mw.wscf.exceptions.InvalidParticipantException ex)
+ {
+ throw new SystemException("UnknownParticipantException");
+ }
+ catch (com.arjuna.mw.wsas.exceptions.SystemException ex)
+ {
+ throw new SystemException(ex.toString());
+ }
+ }
+
+ public void cannotComplete () throws WrongStateException, UnknownTransactionException, SystemException
+ {
+ try
+ {
+ coordinator.participantCannotComplete(participantId);
+ }
+ catch (com.arjuna.mw.wscf.exceptions.InvalidParticipantException ex)
+ {
+ throw new SystemException("UnknownParticipantException");
+ }
+ catch (com.arjuna.mw.wsas.exceptions.WrongStateException ex)
+ {
+ throw new WrongStateException();
+ }
+ catch (com.arjuna.mw.wsas.exceptions.SystemException ex)
+ {
+ throw new SystemException(ex.toString());
+ }
+ }
+
+ /**
+ * this API is not needed here -- quite probably it is not needed in the non recovery case either
+ * @throws SystemException
+ */
+ public void error () throws SystemException
+ {
+ }
+
+ private ACCoordinator coordinator;
+ private String participantId;
+
+}
\ No newline at end of file
Modified: labs/jbosstm/trunk/XTS/demo/dd/jboss/service-web-app.xml
===================================================================
--- labs/jbosstm/trunk/XTS/demo/dd/jboss/service-web-app.xml 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/dd/jboss/service-web-app.xml 2008-11-12 11:08:25 UTC (rev 23835)
@@ -27,6 +27,9 @@
<listener>
<listener-class>com.jboss.jbosstm.xts.demo.services.recovery.DemoATRecoveryListener</listener-class>
</listener>
+ <listener>
+ <listener-class>com.jboss.jbosstm.xts.demo.services.recovery.DemoBARecoveryListener</listener-class>
+ </listener>
<servlet>
<servlet-name>RestaurantServiceAT</servlet-name>
<servlet-class>com.jboss.jbosstm.xts.demo.services.restaurant.RestaurantServiceAT</servlet-class>
Modified: labs/jbosstm/trunk/XTS/demo/ddrpc/jboss/service-web-app.xml
===================================================================
--- labs/jbosstm/trunk/XTS/demo/ddrpc/jboss/service-web-app.xml 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/ddrpc/jboss/service-web-app.xml 2008-11-12 11:08:25 UTC (rev 23835)
@@ -25,6 +25,9 @@
version="2.4">
<listener>
+ <listener-class>com.arjuna.xts.nightout.services.recovery.DemoRPCBARecoveryListener</listener-class>
+ </listener>
+ <listener>
<listener-class>com.arjuna.xts.nightout.services.recovery.DemoRPCATRecoveryListener</listener-class>
</listener>
<servlet>
Added: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoBARecoveryListener.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoBARecoveryListener.java (rev 0)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoBARecoveryListener.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,21 @@
+package com.jboss.jbosstm.xts.demo.services.recovery;
+
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletContextEvent;
+
+/**
+ * Listener to register and unregister teh XTS application specific listener -- we have to
+ * use this because JBossWS does not currently honour the @PostConstruct and @PreDestroy
+ * lifecycle annotations on web services
+ */
+public class DemoBARecoveryListener implements ServletContextListener
+{
+
+ public void contextInitialized(ServletContextEvent event) {
+ DemoBARecoveryModule.register();
+ }
+
+ public void contextDestroyed(ServletContextEvent event) {
+ DemoBARecoveryModule.unregister();
+ }
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoBARecoveryModule.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoBARecoveryModule.java (rev 0)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoBARecoveryModule.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,153 @@
+package com.jboss.jbosstm.xts.demo.services.recovery;
+
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryModule;
+import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
+import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
+
+import java.io.ObjectInputStream;
+
+/**
+ * Application-specific WS-AT participant recovery manager for demo application, This class
+ * is responsible for recreating application-specific durable participants from records
+ * logged at prepare time.
+ */
+public class DemoBARecoveryModule implements XTSBARecoveryModule
+{
+ /**
+ * the singleton recovery module
+ */
+ private static DemoBARecoveryModule theRecoveryModule = null;
+
+ /**
+ * a count of how many xts demo services are currently installed
+ */
+ private static int serviceCount = 0;
+
+ /**
+ * called during deployment of an xts-demo web service to ensure the recovery module for the
+ * demo is installed whenever any of the services is active
+ */
+ public static void register()
+ {
+ if (theRecoveryModule == null) {
+ theRecoveryModule = new DemoBARecoveryModule();
+ }
+ if (serviceCount == 0) {
+ XTSBARecoveryManager.getRecoveryManager().registerRecoveryModule(theRecoveryModule);
+ }
+ serviceCount++;
+ }
+
+ /**
+ * called during undeployment of an xts-demo web service to ensure the recovery module for
+ * the demo is deinstalled once none of the services is active
+ */
+ public static void unregister()
+ {
+ if (serviceCount > 0) {
+ serviceCount--;
+ if (serviceCount == 0) {
+ XTSBARecoveryManager.getRecoveryManager().unregisterRecoveryModule(theRecoveryModule);
+ }
+ }
+ }
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and recreate the participant by deserializing
+ * it from the supplied object input stream. n.b. this is only appropriate in case the
+ * original was a ParticipantCompletion participant saved using serialization.
+ *
+ * @param id the id used when the participant was created
+ * @param stream a stream from which the application should deserialise the participant
+ * if it recognises that the id belongs to the module's application
+ * @return the deserialized ParticipantCompletion participant
+ * @throws Exception if an error occurs deserializing the ParticipantCompletion participant
+ */
+ public BusinessAgreementWithParticipantCompletionParticipant deserializeParticipantCompletionParticipant(String id, ObjectInputStream stream) throws Exception
+ {
+ if (id.startsWith("org.jboss.jbossts.xts-demo:restaurantBA") ||
+ id.startsWith("org.jboss.jbossts.xts-demo:theatreBA") ||
+ id.startsWith("org.jboss.jbossts.xts-demo:taxiBA")) {
+ System.out.println("xts-demo : attempting to deserialize WS-BA participant " + id);
+ BusinessAgreementWithParticipantCompletionParticipant participant = (BusinessAgreementWithParticipantCompletionParticipant)stream.readObject();
+ System.out.println("xts-demo : deserialized WS-BA participant " + id);
+ return participant;
+ }
+ return null;
+ }
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and use the saved recovery state to recreate the
+ * participant. n.b. this is only appropriate in case the original was a ParticipantCompletion
+ * participant saved after being converted to a byte array using the PersistibleBAParticipant
+ * interface.
+ *
+ * @param id the id used when the participant was created
+ * @param recoveryState a byte array returned form the original participant via a call to
+ * method getRecoveryState of interface PersistableBAParticipant
+ * @return the recreated ParticipantCompletion participant
+ * @throws Exception if an error occurs converting the recoveryState back to a
+ * ParticipantCompletion participant
+ */
+ public BusinessAgreementWithParticipantCompletionParticipant recreateParticipantCompletionParticipant(String id, byte[] recoveryState) throws Exception
+ {
+ if (id.startsWith("org.jboss.jbossts.xts-demo:restauarantBA") ||
+ id.startsWith("org.jboss.jbossts.xts-demo:theatreBA") ||
+ id.startsWith("org.jboss.jbossts.xts-demo:taxiBA")) {
+ // this should not get called -- xts-demo WS-BA participants are saved and restored
+ // using serialization
+ throw new Exception("xts-demo : invalid request to recreate() WS-BA participant " + id);
+ }
+ return null;
+ }
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and recreate the participant by deserializing
+ * it from the supplied object input stream. n.b. this is only appropriate in case the
+ * original was a CoordinatorCompletion participant saved using serialization.
+ *
+ * @param id the id used when the participant was created
+ * @param stream a stream from which the application should deserialise the participant
+ * if it recognises that the id belongs to the module's application
+ * @return the deserialized ParticipantCompletion participant
+ * @throws Exception if an error occurs deserializing the CoordinatorCompletion participant
+ */
+ public BusinessAgreementWithCoordinatorCompletionParticipant deserializeCoordinatorCompletionParticipant(String id, ObjectInputStream stream) throws Exception {
+ if (id.startsWith("org.jboss.jbossts.xts-demo:restauarantBA") ||
+ id.startsWith("org.jboss.jbossts.xts-demo:theatreBA") ||
+ id.startsWith("org.jboss.jbossts.xts-demo:taxiBA")) {
+ // this should not get called -- xts-demo WS-BA participants employ the ParticipantCompletion protocol
+ throw new Exception("xts-demo : invalid request to deserialize WS-BA CoordinatorCompletion participant " + id);
+ }
+ return null;
+ }
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and use the saved recovery state to recreate the
+ * participant. n.b. this is only appropriate in case the original was a CoordinatorCompletion
+ * participant saved after being converted to a byte array using the PersistibleBAParticipant
+ * interface.
+ *
+ * @param id the id used when the participant was created
+ * @param recoveryState a byte array returned form the original participant via a call to
+ * method getRecoveryState of interface PersistableBAParticipant
+ * @return the recreated ParticipantCompletion participant
+ * @throws Exception if an error occurs converting the recoveryState back to a
+ * CoordinatorCompletion participant
+ */
+ public BusinessAgreementWithCoordinatorCompletionParticipant recreateCoordinatorCompletionParticipant(String id, byte[] recoveryState) throws Exception {
+ if (id.startsWith("org.jboss.jbossts.xts-demo:restauarantBA") ||
+ id.startsWith("org.jboss.jbossts.xts-demo:theatreBA") ||
+ id.startsWith("org.jboss.jbossts.xts-demo:taxiBA")) {
+ // this should not get called -- xts-demo WS-BA participants are saved and restored
+ // using serialization
+ throw new Exception("xts-demo : invalid request to recreate() WS-BA participant " + id);
+ }
+ return null;
+ }
+}
\ No newline at end of file
Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantManager.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantManager.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -29,6 +29,8 @@
package com.jboss.jbosstm.xts.demo.services.restaurant;
+import com.arjuna.wst.FaultedException;
+
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.*;
@@ -47,8 +49,15 @@
*
* <li>nTotal == nFree + nPrepared + nCommitted
* </ul>
- * changes to nPrepared, nFree, nCommitted, nTotal and preparedList are always shadowed in
- * persistent storage before returning control to clients.
+ * Extended to include support for BA compensation based rollback
+ * </p>
+ * The manager now maintains an extra list compensatableList:
+ * <ul>
+ * <li>nCompensatable == sum(compensatableList.seatCount)
+ * </ul>
+ * changes to nPrepared, nFree, nCommitted, nCompensatable, nTotal, preparedList and compensatableList are
+ * always shadowed in persistent storage before returning control to clients.
+ * </p>
*
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
@@ -202,6 +211,64 @@
}
/**
+ * Compensate a committed booking.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean compensateSeats(Object txID)
+ throws FaultedException
+ {
+ boolean success = false;
+
+ // the transaction must be compensatable
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // see if the user wants to report a compensation fault
+
+ if (!autoCommitMode)
+ {
+ try
+ {
+ // wait for a user commit/rollback decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ if (!isCommit)
+ {
+ throw new FaultedException("RestaurantManager.compensateSeats(): compensation fault");
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println("RestaurantManager.compensateSeats(): Unexpected error during compensation.");
+ throw new FaultedException("RestaurantManager.compensateSeats(): compensation fault");
+ }
+ }
+
+ // compensate the committed transaction
+ Integer request = (Integer) compensatableTransactions.remove(txID);
+ nCompensatableSeats -= request.intValue();
+
+ nCommittedSeats -= request.intValue();
+ nFreeSeats += request.intValue();
+ updateState();
+ success = true;
+ }
+ else
+ {
+ success = false; // error: transaction not registered
+ }
+ return success;
+ }
+
+ /**
* Commit seat bookings.
*
* @param txID The transaction identifier
@@ -209,14 +276,30 @@
*/
public synchronized boolean commitSeats(Object txID)
{
- boolean success = false;
+ return commitSeats(txID, false);
+ }
+ /**
+ * Commit seat bookings, possibly allowing subsequent compensation.
+ *
+ * @param txID The transaction identifier
+ * @param compensatable true if it may be necessary to compensate this commit laer
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean commitSeats(Object txID, boolean compensatable)
+ {
+ boolean success;
+
// the transaction may be prepared, unprepared or unknown
if (preparedTransactions.containsKey(txID))
{
// complete the prepared transaction
Integer request = (Integer) preparedTransactions.remove(txID);
+ if (compensatable) {
+ nCompensatableSeats += request.intValue();
+ compensatableTransactions.put(txID, request);
+ }
nCommittedSeats += request.intValue();
nPreparedSeats -= request.intValue();
nBookedSeats -= request.intValue();
@@ -225,17 +308,88 @@
}
else if (unpreparedTransactions.containsKey(txID))
{
+ Integer request = (Integer) unpreparedTransactions.remove(txID);
+ boolean doCommit;
+ // check we have enough seats and if so
// use one phase commit optimisation, skipping prepare
- Integer request = (Integer) unpreparedTransactions.remove(txID);
- nCommittedSeats += request.intValue();
- nFreeSeats -= request.intValue();
- nBookedSeats -= request.intValue();
+
+ if (autoCommitMode)
+ {
+ if (request.intValue() <= nFreeSeats)
+ {
+ doCommit = true;
+ } else {
+ doCommit = false;
+ }
+ }
+ else
+ {
+ try
+ {
+ // wait for a user decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ doCommit = isCommit;
+ } catch (Exception e) {
+ System.err.println("RestaurantManager.commitSeats(): Unable to perform commit.");
+ doCommit = false;
+ }
+ }
+
+ if (doCommit) {
+ if (compensatable) {
+ nCompensatableSeats += request.intValue();
+ compensatableTransactions.put(txID, request);
+ }
+ nCommittedSeats += request.intValue();
+ nFreeSeats -= request.intValue();
+ nBookedSeats -= request.intValue();
+ updateState();
+ success = true;
+ } else {
+ // get rid of the commitment to keep these seats
+ nBookedSeats -= request.intValue();
+ success = false;
+ }
+ }
+ else
+ {
+ success = false; // error: transaction not registered
+ }
+
+ return success;
+ }
+
+ /**
+ * Close seat bookings, removing possibility for compensation.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean closeSeats(Object txID)
+ {
+ boolean success;
+
+ // the transaction may be compensatable or unknown
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // complete the prepared transaction
+ Integer request = (Integer) compensatableTransactions.remove(txID);
+
+ nCompensatableSeats -= request.intValue();
updateState();
success = true;
}
else
{
- success = false; // error: transaction not registered
+ success = false; // error: transaction not registered for compensation
}
return success;
@@ -315,6 +469,16 @@
}
/**
+ * Get the number of compensatable seats in the given area.
+ *
+ * @return The number of compensatable seats
+ */
+ public int getNCompensatableSeats()
+ {
+ return nCompensatableSeats;
+ }
+
+ /**
* Determine the autoCommit status of the instance.
*
* @return true if autoCommit mode is active, false otherwise
@@ -395,6 +559,8 @@
nBookedSeats = 0;
nPreparedSeats = 0;
nCommittedSeats = 0;
+ nCompensatableSeats = 0;
+ compensatableTransactions = new Hashtable();
preparedTransactions = new Hashtable();
unpreparedTransactions = new Hashtable();
autoCommitMode = true;
@@ -454,6 +620,11 @@
private int nCommittedSeats;
/**
+ * The number of compensatable seats in each area.
+ */
+ private int nCompensatableSeats;
+
+ /**
* The auto commit mode.
* <p/>
* true = automatically commit, false = manually commit
@@ -486,6 +657,11 @@
private Hashtable preparedTransactions;
/**
+ * The transactions we know about and are prepared to compensate.
+ */
+ private Hashtable compensatableTransactions;
+
+ /**
* The default initial capacity of each seating area.
*/
public static final int DEFAULT_SEATING_CAPACITY = 100;
@@ -575,10 +751,18 @@
nFreeSeats = ois.readInt();
nPreparedSeats = ois.readInt();
nCommittedSeats = ois.readInt();
- preparedTransactions = new Hashtable();
+ nCompensatableSeats = ois.readInt();
+ compensatableTransactions = new Hashtable();
String name = (String)ois.readObject();
while (!"".equals(name)) {
int count = ois.readInt();
+ compensatableTransactions.put(name, new Integer(count));
+ name = (String)ois.readObject();
+ }
+ preparedTransactions = new Hashtable();
+ name = (String)ois.readObject();
+ while (!"".equals(name)) {
+ int count = ois.readInt();
preparedTransactions.put(name, new Integer(count));
name = (String)ois.readObject();
}
@@ -602,9 +786,18 @@
oos.writeInt(nFreeSeats);
oos.writeInt(nPreparedSeats);
oos.writeInt(nCommittedSeats);
- Enumeration keys = preparedTransactions.keys();
+ oos.writeInt(nCompensatableSeats);
+ Enumeration keys = compensatableTransactions.keys();
while (keys.hasMoreElements()) {
String name = (String)keys.nextElement();
+ int count = ((Integer)compensatableTransactions.get(name)).intValue();
+ oos.writeObject(name);
+ oos.writeInt(count);
+ }
+ oos.writeObject("");
+ keys = preparedTransactions.keys();
+ while (keys.hasMoreElements()) {
+ String name = (String)keys.nextElement();
int count = ((Integer)preparedTransactions.get(name)).intValue();
oos.writeObject(name);
oos.writeInt(count);
Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantParticipantBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantParticipantBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantParticipantBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -30,8 +30,9 @@
package com.jboss.jbosstm.xts.demo.services.restaurant;
import com.arjuna.wst.*;
-import com.arjuna.ats.arjuna.common.Uid;
+import java.io.Serializable;
+
/**
* An adapter class that exposes the RestaurantManager transaction lifecycle
* API as a WS-T Business Activity participant.
@@ -40,7 +41,7 @@
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
*/
-public class RestaurantParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant
+public class RestaurantParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant, Serializable
{
/**
* Participant instances are related to business method calls
@@ -51,9 +52,6 @@
*/
public RestaurantParticipantBA(String txID, int how_many)
{
- // Binds to the singleton RestaurantView and RestaurantManager
- restaurantManager = RestaurantManager.getSingletonInstance();
- restaurantView = RestaurantView.getSingletonInstance();
// we need to save the txID for later use when logging.
this.txID = txID;
// we also need the business paramater(s) in case of compensation
@@ -70,12 +68,20 @@
public void close() throws WrongStateException, SystemException
{
- // for logging only. This impl does not do anything else here.
+ // let the manager know that this activity no longer requires the option of compensation
System.out.println("RestaurantParticipantBA.close");
- restaurantView.addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
- restaurantView.updateFields();
+ if (!getRestaurantManager().closeSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("RestaurantParticipantBA.close : not expecting a close for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected close for BA participant " + txID);
+ }
+
+ getRestaurantView().addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
+
+ getRestaurantView().updateFields();
}
@@ -90,12 +96,19 @@
public void cancel() throws WrongStateException, SystemException
{
- // we will always have called completed or error, so this can be a null op.
+ // let the manager know that this activity has been cancelled
System.out.println("RestaurantParticipantBA.cancel");
- restaurantView.addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
- restaurantView.updateFields();
+ if (!getRestaurantManager().cancelSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("RestaurantParticipantBA.cancel : not expecting a cancel for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected cancel for BA participant " + txID);
+ }
+
+ getRestaurantView().addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
+ getRestaurantView().updateFields();
}
/**
@@ -111,42 +124,29 @@
{
System.out.println("RestaurantParticipantBA.compensate");
- // Log the event and perform a compensating transaction
- // on the backend business logic.
+ getRestaurantView().addPrepareMessage("id:" + txID + ". Attempting to compensate participant: " + this.getClass().toString());
- restaurantView.addPrepareMessage("id:" + txID + ". Compensate called on participant: " + this.getClass().toString());
- restaurantView.updateFields();
+ getRestaurantView().updateFields();
- if (seatCount > 0)
- {
- String compensatingTxID = new Uid().toString();
- // use a negative number of seats to 'reverse' the previous booking
- // This technique (hack) prevents us needing new business logic to support compensation.
- restaurantManager.bookSeats(compensatingTxID, seatCount * -1);
- restaurantView.updateFields();
+ // tell the manager to compensate
- boolean success = false;
- if(restaurantManager.prepareSeats(compensatingTxID))
- {
- if (restaurantManager.commitSeats(compensatingTxID))
- {
- restaurantView.addMessage("id:" + txID + " Compensating transaction completed sucessfully.");
- restaurantView.updateFields();
- success = true;
- }
- }
- else
- {
- restaurantManager.cancelSeats(compensatingTxID);
- }
+ try {
+ if (!getRestaurantManager().compensateSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("RestaurantParticipantBA.compensate : not expecting a compensate for BA participant " + txID);
- if(!success)
- {
- restaurantView.addMessage("id:" + txID + " Compensation failed. Throwing FaultedException\n");
- restaurantView.updateFields();
- throw new FaultedException("Compensating transaction failed.");
+ throw new WrongStateException("Unexpected compensate for BA participant " + txID);
}
+ } catch (FaultedException fe) {
+ getRestaurantView().addMessage("id:" + txID + ". FaultedException when compensating participant: " + this.getClass().toString());
+
+ getRestaurantView().updateFields();
+ throw fe;
}
+
+ getRestaurantView().addMessage("id:" + txID + ". Compensated participant: " + this.getClass().toString());
+
+ getRestaurantView().updateFields();
}
public String status()
@@ -176,14 +176,12 @@
*/
protected int seatCount;
- /**
- * The RestaurantView object to log events through.
- */
- protected static RestaurantView restaurantView;
+ public RestaurantView getRestaurantView() {
+ return RestaurantView.getSingletonInstance();
+ }
- /**
- * The RestaurantManager to perform business logic operations on.
- */
- protected static RestaurantManager restaurantManager;
+ public RestaurantManager getRestaurantManager() {
+ return RestaurantManager.getSingletonInstance();
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantServiceBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantServiceBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantServiceBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -110,18 +110,19 @@
BAParticipantManager participantManager = null;
try
{
- participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(restaurantParticipant, new Uid().toString());
+ participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(restaurantParticipant, "org.jboss.jbossts.xts-demo:restaurantBA:" + new Uid().toString());
}
catch (Exception e)
{
restaurantView.addMessage("id:" + transactionId + ". Participant enrolement failed");
+ restaurantManager.cancelSeats(transactionId);
System.err.println("bookSeats: Participant enlistment failed");
e.printStackTrace(System.err);
return false;
}
- // finish the booking in the backend:
- restaurantManager.commitSeats(transactionId);
+ // finish the booking in the backend ensuring it is compensatable:
+ restaurantManager.commitSeats(transactionId, true);
try
{
@@ -131,6 +132,7 @@
catch (Exception e)
{
System.err.println("bookSeats: 'completed' callback failed");
+ restaurantManager.cancelSeats(transactionId);
e.printStackTrace(System.err);
return false;
}
Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiManager.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiManager.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -29,6 +29,8 @@
package com.jboss.jbosstm.xts.demo.services.taxi;
+import com.arjuna.wst.FaultedException;
+
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.*;
@@ -38,10 +40,15 @@
* <p/>
* Stores and manages taxi reservations. Knows nothing about Web Services.
* Understands transactional booking lifecycle: unprepared, prepared, finished.
+ * </p>
+ * Extended to include support for BA compensation based rollback
+ * </p>
+ * The manager now maintains an extra list compensatableList
+ * </p>
+ * changes to preparedList and compensatableList are
+ * always shadowed in persistent storage before returning control to clients.
+ * </p>
*
- * </p>changes to preparedList are always shadowed in persistent storage before
- * returning control to clients.
- *
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
*/
@@ -170,6 +177,61 @@
}
/**
+ * Compensate a committed booking.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean compensateTaxi(Object txID)
+ throws FaultedException
+ {
+ boolean success = false;
+
+ // the transaction must be compensatable
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // see if the user wants to report a compensation fault
+
+ if (!autoCommitMode)
+ {
+ try
+ {
+ // wait for a user commit/rollback decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ if (!isCommit)
+ {
+ throw new FaultedException("TheatreManager.compensateSeats(): compensation fault");
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println("TaxiManager.compensateTaxi(): Unexpected error during compensation.");
+ throw new FaultedException("TaxiManager.compensateTaxi(): compensation fault");
+ }
+ }
+
+ // compensate the committed operation
+ compensatableTransactions.remove(txID);
+ updateState();
+ success = true;
+ }
+ else
+ {
+ success = false; // error: transaction not registered
+ }
+
+ return success;
+ }
+
+ /**
* Commit taxi booking.
*
* @param txID The transaction identifier
@@ -177,6 +239,18 @@
*/
public synchronized boolean commitTaxi(Object txID)
{
+ return commitTaxi(txID, false);
+ }
+
+ /**
+ * Commit taxi booking, possibly allowing subsequent compensation.
+ *
+ * @param txID The transaction identifier
+ * @param compensatable true if it may be necessary to compensate this commit laer
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean commitTaxi(Object txID, boolean compensatable)
+ {
boolean success = false;
hasCommitted = true;
@@ -185,16 +259,58 @@
if (preparedTransactions.containsKey(txID))
{
// complete the prepared transaction
- preparedTransactions.remove(txID);
+ Integer request = (Integer)preparedTransactions.remove(txID);
+ if (compensatable) {
+ compensatableTransactions.put(txID, request);
+ }
updateState();
success = true;
}
else if (unpreparedTransactions.containsKey(txID))
{
+ Integer request = (Integer)unpreparedTransactions.remove(txID);
+ boolean doCommit;
+ // check we are ok to go ahead and if so
// use one phase commit optimisation, skipping prepare
- unpreparedTransactions.remove(txID);
- // we don't need to update state
- success = true;
+
+ if (autoCommitMode)
+ {
+ doCommit = true;
+ }
+ else
+ {
+ try
+ {
+ // wait for a user decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ doCommit = isCommit;
+ } catch (Exception e) {
+ System.err.println("RestaurantManager.commitSeats(): Unable to perform commit.");
+ doCommit = false;
+ }
+ }
+
+ if (doCommit) {
+ if (compensatable) {
+ compensatableTransactions.put(txID, request);
+ // we have to update state in this case
+ updateState();
+ success = true;
+ } else {
+ // we don't have to update anything
+ success = true;
+ }
+ } else {
+ // we don't have to update anything
+ success = false;
+ }
}
else
{
@@ -205,6 +321,33 @@
}
/**
+ * Close taxi bookings, removing possibility for compensation.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean closeTaxi(Object txID)
+ {
+ boolean success;
+
+ // the transaction may be compensatable or unknown
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // complete the prepared transaction
+ compensatableTransactions.remove(txID);
+ updateState();
+ success = true;
+ }
+ else
+ {
+ success = false; // error: transaction not registered for compensation
+ }
+
+ return success;
+ }
+
+ /**
* Determine if a specific transaction is known to the business logic.
*
* @param txID The uniq id for the transaction
@@ -290,6 +433,7 @@
*/
public void setToDefault(boolean deleteSavedState)
{
+ compensatableTransactions = new Hashtable();
preparedTransactions = new Hashtable();
unpreparedTransactions = new Hashtable();
autoCommitMode = true;
@@ -347,6 +491,11 @@
private Hashtable preparedTransactions;
/**
+ * The transactions we know about and are prepared to commit.
+ */
+ private Hashtable compensatableTransactions;
+
+ /**
* The auto commit mode.
* <p/>
* true = automatically commit, false = manually commit
@@ -454,10 +603,17 @@
*/
private void readState(ObjectInputStream ois) throws IOException, ClassNotFoundException
{
- preparedTransactions = new Hashtable();
+ compensatableTransactions = new Hashtable();
String name = (String)ois.readObject();
while (!"".equals(name)) {
int count = ois.readInt();
+ compensatableTransactions.put(name, new Integer(count));
+ name = (String)ois.readObject();
+ }
+ preparedTransactions = new Hashtable();
+ name = (String)ois.readObject();
+ while (!"".equals(name)) {
+ int count = ois.readInt();
preparedTransactions.put(name, new Integer(count));
name = (String)ois.readObject();
}
@@ -471,9 +627,17 @@
*/
private void writeState(ObjectOutputStream oos) throws IOException
{
- Enumeration keys = preparedTransactions.keys();
+ Enumeration keys = compensatableTransactions.keys();
while (keys.hasMoreElements()) {
String name = (String)keys.nextElement();
+ int count = ((Integer)compensatableTransactions.get(name)).intValue();
+ oos.writeObject(name);
+ oos.writeInt(count);
+ }
+ oos.writeObject("");
+ keys = preparedTransactions.keys();
+ while (keys.hasMoreElements()) {
+ String name = (String)keys.nextElement();
int count = ((Integer)preparedTransactions.get(name)).intValue();
oos.writeObject(name);
oos.writeInt(count);
Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiParticipantBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiParticipantBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiParticipantBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -31,6 +31,8 @@
import com.arjuna.wst.*;
+import java.io.Serializable;
+
/**
* An adapter class that exposes the TaxiManager transaction lifecycle
* API as a WS-T Business Activity participant.
@@ -39,7 +41,7 @@
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.2 $
*/
-public class TaxiParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant
+public class TaxiParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant, Serializable
{
/**
* Participant instances are related to business method calls
@@ -49,9 +51,6 @@
*/
public TaxiParticipantBA(String txID)
{
- // Binds to the singleton TaxiView and TaxiManager
- taxiManager = TaxiManager.getSingletonInstance();
- taxiView = TaxiView.getSingletonInstance();
// we need to save the txID for later use when logging.
this.txID = txID;
}
@@ -66,12 +65,19 @@
public void close() throws WrongStateException, SystemException
{
- // for logging only. This impl does not do anything else here.
+ // let the manager know that this activity no longer requires the option of compensation
System.out.println("TaxiParticipantBA.close");
- taxiView.addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
- taxiView.updateFields();
+ if (!getTaxiManager().closeTaxi(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("TaxiParticipantBA.close : not expecting a close for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected close for BA participant " + txID);
+ }
+
+ getTaxiView().addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
+ getTaxiView().updateFields();
}
/**
@@ -85,12 +91,19 @@
public void cancel() throws WrongStateException, SystemException
{
- // we will always have called completed or error, so this can be a null op.
+ // let the manager know that this activity is being cancelled
System.out.println("TaxiParticipantBA.cancel");
- taxiView.addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
- taxiView.updateFields();
+ if (!getTaxiManager().cancelTaxi(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("TaxiParticipantBA.cancel : not expecting a cancel for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected cancel for BA participant " + txID);
+ }
+
+ getTaxiView().addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
+ getTaxiView().updateFields();
}
/**
@@ -106,16 +119,31 @@
{
System.out.println("TaxiParticipantBA.compensate");
- // This impl does not support compensation, in order
- // to allow illustration of heuristic outcomes.
- // It just log the event and throws an exception.
+ getTaxiView().addPrepareMessage("id:" + txID + ". Attempting to compensate participant: " + this.getClass().toString());
- taxiView.addMessage("id:" + txID + ". Compensate called on participant: " + this.getClass().toString());
+ getTaxiView().updateFields();
- taxiView.addMessage("Compensation not supported by ths implementation!");
- taxiView.updateFields();
+ // tell the manager to compensate
- throw new FaultedException("Compensation not supported!");
+ try {
+ if (!getTaxiManager().compensateTaxi(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("RestaurantParticipantBA.compensate : not expecting a compensate for BA participant " + txID);
+
+ getTaxiView().addMessage("id:" + txID + ". Failed to compensate participant: " + this.getClass().toString());
+ getTaxiView().updateFields();
+
+ throw new WrongStateException("Unexpected compensate for BA participant " + txID);
+ }
+ } catch (FaultedException fe) {
+ getTaxiView().addMessage("id:" + txID + ". FaultedException when compensating participant: " + this.getClass().toString());
+
+ getTaxiView().updateFields();
+ throw fe;
+ }
+
+ getTaxiView().addMessage("id:" + txID + ". Compensated participant: " + this.getClass().toString());
+ getTaxiView().updateFields();
}
public String status () throws SystemException
@@ -140,13 +168,11 @@
*/
protected String txID;
- /**
- * The TaxiView object to log events through.
- */
- protected static TaxiView taxiView;
+ public TaxiView getTaxiView() {
+ return TaxiView.getSingletonInstance();
+ }
- /**
- * The TaxiManager to perform business logic operations on.
- */
- protected static TaxiManager taxiManager;
+ public TaxiManager getTaxiManager() {
+ return TaxiManager.getSingletonInstance();
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiServiceBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiServiceBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiServiceBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -108,18 +108,19 @@
BAParticipantManager participantManager = null;
try
{
- participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(taxiParticipant, new Uid().toString());
+ participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(taxiParticipant, "org.jboss.jbossts.xts-demo:restaurantBA:" + new Uid().toString());
}
catch (Exception e)
{
taxiView.addMessage("id:" + transactionId + ". Participant enrolement failed");
+ taxiManager.cancelTaxi(transactionId);
System.err.println("bookTaxi: Participant enrolment failed");
e.printStackTrace(System.err);
return false;
}
- // finish the booking in the backend:
- taxiManager.commitTaxi(transactionId);
+ // finish the booking in the backend ensuring it is compensatable:
+ taxiManager.commitTaxi(transactionId, true);
try
{
@@ -129,6 +130,7 @@
catch (Exception e)
{
System.err.println("bookTaxi: 'completed' callback failed");
+ taxiManager.cancelTaxi(transactionId);
e.printStackTrace(System.err);
return false;
}
Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreManager.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreManager.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -29,6 +29,8 @@
package com.jboss.jbosstm.xts.demo.services.theatre;
+import com.arjuna.wst.FaultedException;
+
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.*;
@@ -47,9 +49,15 @@
*
* <li>nTotal[area] == nFree[area] + nPrepared[area] + nCommitted[area]
* </ul>
- * changes to nPrepared, nFree, nCommitted, nTotal and preparedList are always shadowed in
- * persistent storage before returning control to clients.
- *
+ * Extended to include support for BA compensation based rollback
+ * </p>
+ * The manager now maintains an extra list compensatableList:
+ * <ul>
+ * <li>nCompensatable[area] == sum(compensatableList.seatCount[area])
+ * </ul>
+ * changes to nPrepared, nFree, nCommitted, nCompensatable, nTotal, preparedList and compensatableList are
+ * always shadowed in persistent storage before returning control to clients.
+ * </p>
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.4 $
*/
@@ -231,13 +239,86 @@
}
/**
+ * Compensate a booking.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public boolean compensateSeats(Object txID)
+ throws FaultedException
+ {
+ boolean success = false;
+
+ // the transaction must be compensatable
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // see if the user wants to report a compensation fault
+
+ if (!autoCommitMode)
+ {
+ try
+ {
+ // wait for a user commit/rollback decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ if (!isCommit)
+ {
+ throw new FaultedException("TheatreManager.compensateSeats(): compensation fault");
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println("TheatreManager.compensateSeats(): Unexpected error during compensation.");
+ throw new FaultedException("TheatreManager.compensateSeats(): compensation fault");
+ }
+ }
+
+ // compensate the prepared transaction
+ Integer[] requests = (Integer[]) compensatableTransactions.remove(txID);
+ for (int i = 0; i < NUM_SEAT_AREAS; i++)
+ {
+ nCompensatableSeats[i] -= requests[i].intValue();
+ nCommittedSeats[i] -= requests[i].intValue();
+ nFreeSeats[i] += requests[i].intValue();
+ }
+ updateState();
+ success = true;
+ }
+ else
+ {
+ success = false; // error: transaction not registered
+ }
+
+ return success;
+ }
+
+ /**
* Commit seat bookings.
*
* @param txID The transaction identifier
* @return true on success, false otherwise
*/
- public boolean commitSeats(Object txID)
+ public synchronized boolean commitSeats(Object txID)
{
+ return commitSeats(txID, false);
+ }
+
+ /**
+ * Commit seat bookings, possibly allowing subsequent compensation.
+ *
+ * @param txID The transaction identifier
+ * @param compensatable true if it may be necessary to compensate this commit laer
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean commitSeats(Object txID, boolean compensatable)
+ {
boolean success = false;
// the transaction may be prepared, unprepared or unknown
@@ -247,8 +328,15 @@
// complete the prepared transaction
Integer[] requests = (Integer[]) preparedTransactions.remove(txID);
+ if (compensatable)
+ {
+ compensatableTransactions.put(txID, requests);
+ }
for (int i = 0; i < NUM_SEAT_AREAS; i++)
{
+ if (compensatable) {
+ nCompensatableSeats[i] += requests[i].intValue();
+ }
nCommittedSeats[i] += requests[i].intValue();
nPreparedSeats[i] -= requests[i].intValue();
nBookedSeats[i] -= requests[i].intValue();
@@ -260,18 +348,97 @@
{
// use one phase commit optimisation, skipping prepare
Integer[] requests = (Integer[]) unpreparedTransactions.remove(txID);
- for (int i = 0; i < NUM_SEAT_AREAS; i++)
+ boolean doCommit = true;
+ // check we have enough seats and if so
+ // use one phase commit optimisation, skipping prepare
+
+ if (autoCommitMode)
{
- nCommittedSeats[i] += requests[i].intValue();
- nFreeSeats[i] -= requests[i].intValue();
- nBookedSeats[i] -= requests[i].intValue();
+ for (int i = 0; doCommit && i < NUM_SEAT_AREAS; i++)
+ {
+ if (requests[i].intValue() > nFreeSeats[i])
+ {
+ doCommit = false;
+ }
+ }
}
+ else
+ {
+ try
+ {
+ // wait for a user decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ doCommit = isCommit;
+ } catch (Exception e) {
+ System.err.println("TheatreManager.commitSeats(): Unable to perform commit.");
+ doCommit = false;
+ }
+ }
+
+ if (doCommit) {
+ if (compensatable) {
+ compensatableTransactions.put(txID, requests);
+ }
+ for (int i = 0; i < NUM_SEAT_AREAS; i++)
+ {
+ if (compensatable) {
+ nCompensatableSeats[i] += requests[i].intValue();
+ }
+ nCommittedSeats[i] += requests[i].intValue();
+ nFreeSeats[i] -= requests[i].intValue();
+ nBookedSeats[i] -= requests[i].intValue();
+ }
+ updateState();
+ success = true;
+ } else {
+ // get rid of the commitment to keep these seats
+ for (int i = 0; i < NUM_SEAT_AREAS; i++)
+ {
+ nBookedSeats[i] -= requests[i].intValue();
+ }
+ success = false;
+ }
+ }
+ else
+ {
+ success = false; // error: transaction not registered
+ }
+
+ return success;
+ }
+
+ /**
+ * Close seat bookings, removing possibility for compensation.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean closeSeats(Object txID)
+ {
+ boolean success;
+
+ // the transaction may be compensatable or unknown
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // complete the prepared transaction
+ Integer[] requests = (Integer[]) compensatableTransactions.remove(txID);
+ for (int i = 0; i < NUM_SEAT_AREAS; i++) {
+ nCompensatableSeats[i] -= requests[i].intValue();
+ }
updateState();
success = true;
}
else
{
- success = false; // error: transaction not registered
+ success = false; // error: transaction not registered for compensation
}
return success;
@@ -357,6 +524,16 @@
}
/**
+ * Get the number of compensatable seats in the given area.
+ *
+ * @return The number of compensatable seats
+ */
+ public int getNCompensatableSeats(int area)
+ {
+ return nCompensatableSeats[area];
+ }
+
+ /**
* Determine the autoCommit status of the instance.
*
* @return true if autoCommit mode is active, false otherwise
@@ -434,6 +611,7 @@
nBookedSeats = new int[NUM_SEAT_AREAS];
nPreparedSeats = new int[NUM_SEAT_AREAS];
nCommittedSeats = new int[NUM_SEAT_AREAS];
+ nCompensatableSeats = new int[NUM_SEAT_AREAS];
for (int i = 0; i < NUM_SEAT_AREAS; i++)
{
nTotalSeats[i] = DEFAULT_SEATING_CAPACITY;
@@ -441,7 +619,9 @@
nBookedSeats[i] = 0;
nPreparedSeats[i] = 0;
nCommittedSeats[i] = 0;
+ nCompensatableSeats[i] = 0;
}
+ compensatableTransactions = new Hashtable();
preparedTransactions = new Hashtable();
unpreparedTransactions = new Hashtable();
autoCommitMode = true;
@@ -506,6 +686,11 @@
private int[] nCommittedSeats;
/**
+ * The number of compensatable seats in each area.
+ */
+ private int[] nCompensatableSeats;
+
+ /**
* The auto commit mode.
* <p/>
* true = automatically commit, false = manually commit
@@ -538,6 +723,11 @@
private Hashtable preparedTransactions;
/**
+ * The transactions we know about and are prepared to compensate.
+ */
+ private Hashtable compensatableTransactions;
+
+ /**
* Constant (array index) used for the seating area CIRCLE.
*/
public static final int CIRCLE = 0;
@@ -648,8 +838,9 @@
nFreeSeats[i] = ois.readInt();
nPreparedSeats[i] = ois.readInt();
nCommittedSeats[i] = ois.readInt();
+ nCompensatableSeats[i] = ois.readInt();
}
- preparedTransactions = new Hashtable();
+ compensatableTransactions = new Hashtable();
String name = (String)ois.readObject();
while (!"".equals(name)) {
Integer[] counts = new Integer[NUM_SEAT_AREAS];
@@ -657,6 +848,17 @@
int count = ois.readInt();
counts[i] = new Integer(count);
}
+ compensatableTransactions.put(name, counts);
+ name = (String)ois.readObject();
+ }
+ preparedTransactions = new Hashtable();
+ name = (String)ois.readObject();
+ while (!"".equals(name)) {
+ Integer[] counts = new Integer[NUM_SEAT_AREAS];
+ for (int i = 0; i < NUM_SEAT_AREAS; i++) {
+ int count = ois.readInt();
+ counts[i] = new Integer(count);
+ }
preparedTransactions.put(name, counts);
name = (String)ois.readObject();
}
@@ -683,10 +885,21 @@
oos.writeInt(nFreeSeats[i]);
oos.writeInt(nPreparedSeats[i]);
oos.writeInt(nCommittedSeats[i]);
+ oos.writeInt(nCompensatableSeats[i]);
}
- Enumeration keys = preparedTransactions.keys();
+ Enumeration keys = compensatableTransactions.keys();
while (keys.hasMoreElements()) {
String name = (String)keys.nextElement();
+ Integer[] counts = (Integer[]) compensatableTransactions.get(name);
+ oos.writeObject(name);
+ for (int i = 0; i < NUM_SEAT_AREAS; i++) {
+ oos.writeInt(counts[i].intValue());
+ }
+ }
+ oos.writeObject("");
+ keys = preparedTransactions.keys();
+ while (keys.hasMoreElements()) {
+ String name = (String)keys.nextElement();
Integer[] counts = (Integer[]) preparedTransactions.get(name);
oos.writeObject(name);
for (int i = 0; i < NUM_SEAT_AREAS; i++) {
Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreParticipantBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreParticipantBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreParticipantBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -32,6 +32,8 @@
import com.arjuna.wst.*;
import com.arjuna.ats.arjuna.common.Uid;
+import java.io.Serializable;
+
/**
* An adapter class that exposes the TheatreManager transaction lifecycle
* API as a WS-T Business Activity participant.
@@ -40,7 +42,7 @@
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
*/
-public class TheatreParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant
+public class TheatreParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant, Serializable
{
/**
* Participant instances are related to business method calls
@@ -52,9 +54,6 @@
*/
public TheatreParticipantBA(String txID, int how_many, int which_area)
{
- // Binds to the singleton TheatreView and TheatreManager
- theatreManager = TheatreManager.getSingletonInstance();
- theatreView = TheatreView.getSingletonInstance();
// we need to save the txID for later use when logging
this.txID = txID;
// we also need the business paramater(s) in case of compensation
@@ -72,12 +71,19 @@
public void close() throws WrongStateException, SystemException
{
- // for logging only. This impl does not do anything else here.
+ // let the manager know that this activity no longer requires the option of compensation
System.out.println("TheatreParticipantBA.close");
- theatreView.addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
- theatreView.updateFields();
+ if (!getTheatreManager().closeSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("TheatreParticipantBA.close : not expecting a close for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected close for BA participant " + txID);
+ }
+
+ getTheatreView().addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
+ getTheatreView().updateFields();
}
/**
@@ -91,12 +97,19 @@
public void cancel() throws WrongStateException, SystemException
{
- // we will always have called completed or error, so this can be a null op.
+ // let the manager know that this activity has been cancelled
System.out.println("TheatreParticipantBA.cancel");
- theatreView.addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
- theatreView.updateFields();
+ if (!getTheatreManager().cancelSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("TheatreParticipantBA.cancel : not expecting a cancel for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected cancel for BA participant " + txID);
+ }
+
+ getTheatreView().addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
+ getTheatreView().updateFields();
}
/**
@@ -112,42 +125,29 @@
{
System.out.println("TheatreParticipantBA.compensate");
- // Log the event and perform a compensating transaction
- // on the backend business logic if needed.
+ getTheatreView().addPrepareMessage("id:" + txID + ". Attempting to compensate participant: " + this.getClass().toString());
- theatreView.addPrepareMessage("id:" + txID + ". Compensate called on participant: " + this.getClass().toString());
- theatreView.updateFields();
+ getTheatreView().updateFields();
- if (seatCount > 0)
- {
- String compensatingTxID = new Uid().toString();
- // use a negative number of seats to 'reverse' the previous booking
- // This technique (hack) prevents us needing new business logic to support compensation.
- theatreManager.bookSeats(compensatingTxID, seatCount * -1, seatingArea);
- theatreView.updateFields();
+ // tell the manager to compensate
- boolean success = false;
- if(theatreManager.prepareSeats(compensatingTxID))
- {
- if (theatreManager.commitSeats(compensatingTxID))
- {
- theatreView.addMessage("id:" + txID + " Compensating transaction completed sucessfully.");
- theatreView.updateFields();
- success = true;
- }
- }
- else
- {
- theatreManager.cancelSeats(compensatingTxID);
- }
+ try {
+ if (!getTheatreManager().compensateSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("TheatreParticipantBA.compensate : not expecting a compensate for BA participant " + txID);
- if(!success)
- {
- theatreView.addMessage("id:" + txID + " Compensation failed. Throwing FaultedException\n");
- theatreView.updateFields();
- throw new FaultedException("Compensating transaction failed.");
+ throw new WrongStateException("Unexpected compensate for BA participant " + txID);
}
+ } catch (FaultedException fe) {
+ getTheatreView().addMessage("id:" + txID + ". FaultedException when compensating participant: " + this.getClass().toString());
+
+ getTheatreView().updateFields();
+ throw fe;
}
+
+ getTheatreView().addMessage("id:" + txID + ". Compensated participant: " + this.getClass().toString());
+
+ getTheatreView().updateFields();
}
public String status()
@@ -182,13 +182,11 @@
*/
protected int seatingArea;
- /**
- * The TheatreView object to log events through.
- */
- protected static TheatreView theatreView;
+ public TheatreView getTheatreView() {
+ return TheatreView.getSingletonInstance();
+ }
- /**
- * The TheatreManager to perform business logic operations on.
- */
- protected static TheatreManager theatreManager;
+ public TheatreManager getTheatreManager() {
+ return TheatreManager.getSingletonInstance();
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreServiceBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreServiceBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreServiceBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -108,17 +108,19 @@
BAParticipantManager participantManager = null;
try
{
- participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(theatreParticipant, new Uid().toString());
+ participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(theatreParticipant, "org.jboss.jbossts.xts-demo:restaurantBA:" + new Uid().toString());
}
catch (Exception e)
{
theatreView.addMessage("id:" + transactionId + ". Participant enrolement failed");
+ theatreManager.cancelSeats(transactionId);
System.err.println("bookSeats: Participant enrolement failed");
e.printStackTrace(System.err);
return false;
}
- theatreManager.commitSeats(transactionId);
+ // finish the booking in the backend ensuring it is compensatable:
+ theatreManager.commitSeats(transactionId, true);
try
{
@@ -127,6 +129,7 @@
catch (Exception e)
{
System.err.println("bookSeats: 'completed' callback failed");
+ theatreManager.cancelSeats(transactionId);
e.printStackTrace(System.err);
return false;
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantManager.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantManager.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -29,6 +29,8 @@
package com.arjuna.xts.nightout.services.Restaurant;
+import com.arjuna.wst.FaultedException;
+
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.*;
@@ -47,8 +49,15 @@
*
* <li>nTotal == nFree + nPrepared + nCommitted
* </ul>
- * changes to nPrepared, nFree, nCommitted, nTotal and preparedList are always shadowed in
- * persistent storage before returning control to clients.
+ * Extended to include support for BA compensation based rollback
+ * </p>
+ * The manager now maintains an extra list compensatableList:
+ * <ul>
+ * <li>nCompensatable == sum(compensatableList.seatCount)
+ * </ul>
+ * changes to nPrepared, nFree, nCommitted, nCompensatable, nTotal, preparedList and compensatableList are
+ * always shadowed in persistent storage before returning control to clients.
+ * </p>
*
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
@@ -202,6 +211,64 @@
}
/**
+ * Compensate a committed booking.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean compensateSeats(Object txID)
+ throws FaultedException
+ {
+ boolean success = false;
+
+ // the transaction must be compensatable
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // see if the user wants to report a compensation fault
+
+ if (!autoCommitMode)
+ {
+ try
+ {
+ // wait for a user commit/rollback decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ if (!isCommit)
+ {
+ throw new FaultedException("RestaurantManager.compensateSeats(): compensation fault");
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println("RestaurantManager.compensateSeats(): Unexpected error during compensation.");
+ throw new FaultedException("RestaurantManager.compensateSeats(): compensation fault");
+ }
+ }
+
+ // compensate the committed transaction
+ Integer request = (Integer) compensatableTransactions.remove(txID);
+ nCompensatableSeats -= request.intValue();
+
+ nCommittedSeats -= request.intValue();
+ nFreeSeats += request.intValue();
+ updateState();
+ success = true;
+ }
+ else
+ {
+ success = false; // error: transaction not registered
+ }
+ return success;
+ }
+
+ /**
* Commit seat bookings.
*
* @param txID The transaction identifier
@@ -209,14 +276,30 @@
*/
public synchronized boolean commitSeats(Object txID)
{
- boolean success = false;
+ return commitSeats(txID, false);
+ }
+ /**
+ * Commit seat bookings, possibly allowing subsequent compensation.
+ *
+ * @param txID The transaction identifier
+ * @param compensatable true if it may be necessary to compensate this commit laer
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean commitSeats(Object txID, boolean compensatable)
+ {
+ boolean success;
+
// the transaction may be prepared, unprepared or unknown
if (preparedTransactions.containsKey(txID))
{
// complete the prepared transaction
Integer request = (Integer) preparedTransactions.remove(txID);
+ if (compensatable) {
+ nCompensatableSeats += request.intValue();
+ compensatableTransactions.put(txID, request);
+ }
nCommittedSeats += request.intValue();
nPreparedSeats -= request.intValue();
nBookedSeats -= request.intValue();
@@ -225,11 +308,82 @@
}
else if (unpreparedTransactions.containsKey(txID))
{
+ Integer request = (Integer) unpreparedTransactions.remove(txID);
+ boolean doCommit;
+ // check we have enough seats and if so
// use one phase commit optimisation, skipping prepare
- Integer request = (Integer) unpreparedTransactions.remove(txID);
- nCommittedSeats += request.intValue();
- nFreeSeats -= request.intValue();
- nBookedSeats -= request.intValue();
+
+ if (autoCommitMode)
+ {
+ if (request.intValue() <= nFreeSeats)
+ {
+ doCommit = true;
+ } else {
+ doCommit = false;
+ }
+ }
+ else
+ {
+ try
+ {
+ // wait for a user decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ doCommit = isCommit;
+ } catch (Exception e) {
+ System.err.println("RestaurantManager.commitSeats(): Unable to perform commit.");
+ doCommit = false;
+ }
+ }
+
+ if (doCommit) {
+ if (compensatable) {
+ nCompensatableSeats += request.intValue();
+ compensatableTransactions.put(txID, request);
+ }
+ nCommittedSeats += request.intValue();
+ nFreeSeats -= request.intValue();
+ nBookedSeats -= request.intValue();
+ updateState();
+ success = true;
+ } else {
+ // get rid of the commitment to keep these seats
+ nBookedSeats -= request.intValue();
+ success = false;
+ }
+ }
+ else
+ {
+ success = false; // error: transaction not registered for compensation
+ }
+
+ return success;
+ }
+
+ /**
+ * Close seat bookings, removing possibility for compensation.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean closeSeats(Object txID)
+ {
+ boolean success;
+
+ // the transaction may be compensatable or unknown
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // complete the prepared transaction
+ Integer request = (Integer) compensatableTransactions.remove(txID);
+
+ nCompensatableSeats -= request.intValue();
updateState();
success = true;
}
@@ -315,6 +469,16 @@
}
/**
+ * Get the number of compensatable seats in the given area.
+ *
+ * @return The number of compensatable seats
+ */
+ public int getNCompensatableSeats()
+ {
+ return nCompensatableSeats;
+ }
+
+ /**
* Determine the autoCommit status of the instance.
*
* @return true if autoCommit mode is active, false otherwise
@@ -395,6 +559,8 @@
nBookedSeats = 0;
nPreparedSeats = 0;
nCommittedSeats = 0;
+ nCompensatableSeats = 0;
+ compensatableTransactions = new Hashtable();
preparedTransactions = new Hashtable();
unpreparedTransactions = new Hashtable();
autoCommitMode = true;
@@ -454,6 +620,11 @@
private int nCommittedSeats;
/**
+ * The number of compensatable seats in each area.
+ */
+ private int nCompensatableSeats;
+
+ /**
* The auto commit mode.
* <p/>
* true = automatically commit, false = manually commit
@@ -486,6 +657,11 @@
private Hashtable preparedTransactions;
/**
+ * The transactions we know about and are prepared to compensate.
+ */
+ private Hashtable compensatableTransactions;
+
+ /**
* The default initial capacity of each seating area.
*/
public static final int DEFAULT_SEATING_CAPACITY = 100;
@@ -575,10 +751,18 @@
nFreeSeats = ois.readInt();
nPreparedSeats = ois.readInt();
nCommittedSeats = ois.readInt();
- preparedTransactions = new Hashtable();
+ nCompensatableSeats = ois.readInt();
+ compensatableTransactions = new Hashtable();
String name = (String)ois.readObject();
while (!"".equals(name)) {
int count = ois.readInt();
+ compensatableTransactions.put(name, new Integer(count));
+ name = (String)ois.readObject();
+ }
+ preparedTransactions = new Hashtable();
+ name = (String)ois.readObject();
+ while (!"".equals(name)) {
+ int count = ois.readInt();
preparedTransactions.put(name, new Integer(count));
name = (String)ois.readObject();
}
@@ -602,9 +786,18 @@
oos.writeInt(nFreeSeats);
oos.writeInt(nPreparedSeats);
oos.writeInt(nCommittedSeats);
- Enumeration keys = preparedTransactions.keys();
+ oos.writeInt(nCompensatableSeats);
+ Enumeration keys = compensatableTransactions.keys();
while (keys.hasMoreElements()) {
String name = (String)keys.nextElement();
+ int count = ((Integer)compensatableTransactions.get(name)).intValue();
+ oos.writeObject(name);
+ oos.writeInt(count);
+ }
+ oos.writeObject("");
+ keys = preparedTransactions.keys();
+ while (keys.hasMoreElements()) {
+ String name = (String)keys.nextElement();
int count = ((Integer)preparedTransactions.get(name)).intValue();
oos.writeObject(name);
oos.writeInt(count);
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantParticipantBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantParticipantBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantParticipantBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -30,8 +30,9 @@
package com.arjuna.xts.nightout.services.Restaurant;
import com.arjuna.wst.*;
-import com.arjuna.ats.arjuna.common.Uid;
+import java.io.Serializable;
+
/**
* An adapter class that exposes the RestaurantManager transaction lifecycle
* API as a WS-T Business Activity participant.
@@ -40,7 +41,7 @@
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
*/
-public class RestaurantParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant
+public class RestaurantParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant, Serializable
{
/**
* Participant instances are related to business method calls
@@ -51,9 +52,6 @@
*/
public RestaurantParticipantBA(String txID, int how_many)
{
- // Binds to the singleton RestaurantView and RestaurantManager
- restaurantManager = RestaurantManager.getSingletonInstance();
- restaurantView = RestaurantView.getSingletonInstance();
// we need to save the txID for later use when logging.
this.txID = txID;
// we also need the business paramater(s) in case of compensation
@@ -70,12 +68,20 @@
public void close() throws WrongStateException, SystemException
{
- // for logging only. This impl does not do anything else here.
+ // let the manager know that this activity no longer requires the option of compensation
System.out.println("RestaurantParticipantBA.close");
- restaurantView.addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
- restaurantView.updateFields();
+ if (!getRestaurantManager().closeSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("RestaurantParticipantBA.close : not expecting a close for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected close for BA participant " + txID);
+ }
+
+ getRestaurantView().addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
+
+ getRestaurantView().updateFields();
}
@@ -90,12 +96,19 @@
public void cancel() throws WrongStateException, SystemException
{
- // we will always have called completed or error, so this can be a null op.
+ // let the manager know that this activity has been cancelled
System.out.println("RestaurantParticipantBA.cancel");
- restaurantView.addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
- restaurantView.updateFields();
+ if (!getRestaurantManager().cancelSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("RestaurantParticipantBA.cancel : not expecting a cancel for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected cancel for BA participant " + txID);
+ }
+
+ getRestaurantView().addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
+ getRestaurantView().updateFields();
}
/**
@@ -114,39 +127,29 @@
// Log the event and perform a compensating transaction
// on the backend business logic.
- restaurantView.addPrepareMessage("id:" + txID + ". Compensate called on participant: " + this.getClass().toString());
- restaurantView.updateFields();
+ getRestaurantView().addPrepareMessage("id:" + txID + ". Compensate called on participant: " + this.getClass().toString());
- if (seatCount > 0)
- {
- String compensatingTxID = new Uid().toString();
- // use a negative number of seats to 'reverse' the previous booking
- // This technique (hack) prevents us needing new business logic to support compensation.
- restaurantManager.bookSeats(compensatingTxID, seatCount * -1);
- restaurantView.updateFields();
+ getRestaurantView().updateFields();
- boolean success = false;
- if(restaurantManager.prepareSeats(compensatingTxID))
- {
- if (restaurantManager.commitSeats(compensatingTxID))
- {
- restaurantView.addMessage("id:" + txID + " Compensating transaction completed sucessfully.");
- restaurantView.updateFields();
- success = true;
- }
+ // tell the manager to compensate
+
+ try {
+ if (!getRestaurantManager().compensateSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("RestaurantParticipantBA.compensate : not expecting a compensate for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected compensate for BA participant " + txID);
}
- else
- {
- restaurantManager.cancelSeats(compensatingTxID);
- }
+ } catch (FaultedException fe) {
+ getRestaurantView().addMessage("id:" + txID + ". FaultedException when compensating participant: " + this.getClass().toString());
- if(!success)
- {
- restaurantView.addMessage("id:" + txID + " Compensation failed. Throwing FaultedException\n");
- restaurantView.updateFields();
- throw new FaultedException("Compensating transaction failed.");
- }
+ getRestaurantView().updateFields();
+ throw fe;
}
+
+ getRestaurantView().addMessage("id:" + txID + ". Compensated participant: " + this.getClass().toString());
+
+ getRestaurantView().updateFields();
}
public String status()
@@ -176,14 +179,12 @@
*/
protected int seatCount;
- /**
- * The RestaurantView object to log events through.
- */
- protected static RestaurantView restaurantView;
+ public RestaurantView getRestaurantView() {
+ return RestaurantView.getSingletonInstance();
+ }
- /**
- * The RestaurantManager to perform business logic operations on.
- */
- protected static RestaurantManager restaurantManager;
+ public RestaurantManager getRestaurantManager() {
+ return RestaurantManager.getSingletonInstance();
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantServiceBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantServiceBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantServiceBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -96,18 +96,19 @@
BAParticipantManager participantManager = null;
try
{
- participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(restaurantParticipant, new Uid().toString());
+ participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(restaurantParticipant, "com.arjuna.xts-demorpc:restaurantBA:" + new Uid().toString());
}
catch (Exception e)
{
restaurantView.addMessage("id:" + transactionId + ". Participant enrolement failed");
+ restaurantManager.cancelSeats(transactionId);
System.err.println("bookSeats: Participant enlistment failed");
e.printStackTrace(System.err);
return false;
}
- // finish the booking in the backend:
- restaurantManager.commitSeats(transactionId);
+ // finish the booking in the backend ensuring it is compensatable:
+ restaurantManager.commitSeats(transactionId, true);
try
{
@@ -117,6 +118,7 @@
catch (Exception e)
{
System.err.println("bookSeats: 'completed' callback failed");
+ restaurantManager.cancelSeats(transactionId);
e.printStackTrace(System.err);
return false;
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiManager.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiManager.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -29,6 +29,8 @@
package com.arjuna.xts.nightout.services.Taxi;
+import com.arjuna.wst.FaultedException;
+
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.*;
@@ -38,10 +40,15 @@
* <p/>
* Stores and manages taxi reservations. Knows nothing about Web Services.
* Understands transactional booking lifecycle: unprepared, prepared, finished.
+ * </p>
+ * Extended to include support for BA compensation based rollback
+ * </p>
+ * The manager now maintains an extra list compensatableList
+ * </p>
+ * changes to preparedList and compensatableList are
+ * always shadowed in persistent storage before returning control to clients.
+ * </p>
*
- * </p>changes to preparedList are always shadowed in persistent storage before
- * returning control to clients.
- *
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
*/
@@ -170,6 +177,61 @@
}
/**
+ * Compensate a committed booking.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean compensateTaxi(Object txID)
+ throws FaultedException
+ {
+ boolean success = false;
+
+ // the transaction must be compensatable
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // see if the user wants to report a compensation fault
+
+ if (!autoCommitMode)
+ {
+ try
+ {
+ // wait for a user commit/rollback decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ if (!isCommit)
+ {
+ throw new FaultedException("TheatreManager.compensateSeats(): compensation fault");
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println("TaxiManager.compensateTaxi(): Unexpected error during compensation.");
+ throw new FaultedException("TaxiManager.compensateTaxi(): compensation fault");
+ }
+ }
+
+ // compensate the committed operation
+ compensatableTransactions.remove(txID);
+ updateState();
+ success = true;
+ }
+ else
+ {
+ success = false; // error: transaction not registered
+ }
+
+ return success;
+ }
+
+ /**
* Commit taxi booking.
*
* @param txID The transaction identifier
@@ -177,6 +239,18 @@
*/
public synchronized boolean commitTaxi(Object txID)
{
+ return commitTaxi(txID, false);
+ }
+
+ /**
+ * Commit taxi booking, possibly allowing subsequent compensation.
+ *
+ * @param txID The transaction identifier
+ * @param compensatable true if it may be necessary to compensate this commit laer
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean commitTaxi(Object txID, boolean compensatable)
+ {
boolean success = false;
hasCommitted = true;
@@ -185,16 +259,58 @@
if (preparedTransactions.containsKey(txID))
{
// complete the prepared transaction
- preparedTransactions.remove(txID);
+ Integer request = (Integer)preparedTransactions.remove(txID);
+ if (compensatable) {
+ compensatableTransactions.put(txID, request);
+ }
updateState();
success = true;
}
else if (unpreparedTransactions.containsKey(txID))
{
+ Integer request = (Integer)unpreparedTransactions.remove(txID);
+ boolean doCommit;
+ // check we are ok to go ahead and if so
// use one phase commit optimisation, skipping prepare
- unpreparedTransactions.remove(txID);
- // we don't need to update state
- success = true;
+
+ if (autoCommitMode)
+ {
+ doCommit = true;
+ }
+ else
+ {
+ try
+ {
+ // wait for a user decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ doCommit = isCommit;
+ } catch (Exception e) {
+ System.err.println("RestaurantManager.commitSeats(): Unable to perform commit.");
+ doCommit = false;
+ }
+ }
+
+ if (doCommit) {
+ if (compensatable) {
+ compensatableTransactions.put(txID, request);
+ // we have to update state in this case
+ updateState();
+ success = true;
+ } else {
+ // we don't have to update anything
+ success = true;
+ }
+ } else {
+ // we don't have to update anything
+ success = false;
+ }
}
else
{
@@ -205,6 +321,33 @@
}
/**
+ * Close taxi bookings, removing possibility for compensation.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean closeTaxi(Object txID)
+ {
+ boolean success;
+
+ // the transaction may be compensatable or unknown
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // complete the prepared transaction
+ compensatableTransactions.remove(txID);
+ updateState();
+ success = true;
+ }
+ else
+ {
+ success = false; // error: transaction not registered for compensation
+ }
+
+ return success;
+ }
+
+ /**
* Determine if a specific transaction is known to the business logic.
*
* @param txID The uniq id for the transaction
@@ -290,6 +433,7 @@
*/
public void setToDefault(boolean deleteSavedState)
{
+ compensatableTransactions = new Hashtable();
preparedTransactions = new Hashtable();
unpreparedTransactions = new Hashtable();
autoCommitMode = true;
@@ -347,6 +491,11 @@
private Hashtable preparedTransactions;
/**
+ * The transactions we know about and are prepared to commit.
+ */
+ private Hashtable compensatableTransactions;
+
+ /**
* The auto commit mode.
* <p/>
* true = automatically commit, false = manually commit
@@ -454,10 +603,17 @@
*/
private void readState(ObjectInputStream ois) throws IOException, ClassNotFoundException
{
- preparedTransactions = new Hashtable();
+ compensatableTransactions = new Hashtable();
String name = (String)ois.readObject();
while (!"".equals(name)) {
int count = ois.readInt();
+ compensatableTransactions.put(name, new Integer(count));
+ name = (String)ois.readObject();
+ }
+ preparedTransactions = new Hashtable();
+ name = (String)ois.readObject();
+ while (!"".equals(name)) {
+ int count = ois.readInt();
preparedTransactions.put(name, new Integer(count));
name = (String)ois.readObject();
}
@@ -471,9 +627,17 @@
*/
private void writeState(ObjectOutputStream oos) throws IOException
{
- Enumeration keys = preparedTransactions.keys();
+ Enumeration keys = compensatableTransactions.keys();
while (keys.hasMoreElements()) {
String name = (String)keys.nextElement();
+ int count = ((Integer)compensatableTransactions.get(name)).intValue();
+ oos.writeObject(name);
+ oos.writeInt(count);
+ }
+ oos.writeObject("");
+ keys = preparedTransactions.keys();
+ while (keys.hasMoreElements()) {
+ String name = (String)keys.nextElement();
int count = ((Integer)preparedTransactions.get(name)).intValue();
oos.writeObject(name);
oos.writeInt(count);
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiParticipantBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiParticipantBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiParticipantBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -31,6 +31,8 @@
import com.arjuna.wst.*;
+import java.io.Serializable;
+
/**
* An adapter class that exposes the TaxiManager transaction lifecycle
* API as a WS-T Business Activity participant.
@@ -39,7 +41,7 @@
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.2 $
*/
-public class TaxiParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant
+public class TaxiParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant, Serializable
{
/**
* Participant instances are related to business method calls
@@ -49,9 +51,6 @@
*/
public TaxiParticipantBA(String txID)
{
- // Binds to the singleton TaxiView and TaxiManager
- taxiManager = TaxiManager.getSingletonInstance();
- taxiView = TaxiView.getSingletonInstance();
// we need to save the txID for later use when logging.
this.txID = txID;
}
@@ -66,12 +65,19 @@
public void close() throws WrongStateException, SystemException
{
- // for logging only. This impl does not do anything else here.
+ // let the manager know that this activity no longer requires the option of compensation
System.out.println("TaxiParticipantBA.close");
- taxiView.addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
- taxiView.updateFields();
+ if (!getTaxiManager().closeTaxi(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("TaxiParticipantBA.close : not expecting a close for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected close for BA participant " + txID);
+ }
+
+ getTaxiView().addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
+ getTaxiView().updateFields();
}
/**
@@ -85,12 +91,19 @@
public void cancel() throws WrongStateException, SystemException
{
- // we will always have called completed or error, so this can be a null op.
+ // let the manager know that this activity is being cancelled
System.out.println("TaxiParticipantBA.cancel");
- taxiView.addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
- taxiView.updateFields();
+ if (!getTaxiManager().cancelTaxi(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("TaxiParticipantBA.cancel : not expecting a cancel for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected cancel for BA participant " + txID);
+ }
+
+ getTaxiView().addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
+ getTaxiView().updateFields();
}
/**
@@ -106,16 +119,31 @@
{
System.out.println("TaxiParticipantBA.compensate");
- // This impl does not support compensation, in order
- // to allow illustration of heuristic outcomes.
- // It just log the event and throws an exception.
+ getTaxiView().addPrepareMessage("id:" + txID + ". Attempting to compensate participant: " + this.getClass().toString());
- taxiView.addMessage("id:" + txID + ". Compensate called on participant: " + this.getClass().toString());
+ getTaxiView().updateFields();
- taxiView.addMessage("Compensation not supported by ths implementation!");
- taxiView.updateFields();
+ // tell the manager to compensate
- throw new FaultedException("Compensation not supported!");
+ try {
+ if (!getTaxiManager().compensateTaxi(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("RestaurantParticipantBA.compensate : not expecting a compensate for BA participant " + txID);
+
+ getTaxiView().addMessage("id:" + txID + ". Failed to compensate participant: " + this.getClass().toString());
+ getTaxiView().updateFields();
+
+ throw new WrongStateException("Unexpected compensate for BA participant " + txID);
+ }
+ } catch (FaultedException fe) {
+ getTaxiView().addMessage("id:" + txID + ". FaultedException when compensating participant: " + this.getClass().toString());
+
+ getTaxiView().updateFields();
+ throw fe;
+ }
+
+ getTaxiView().addMessage("id:" + txID + ". Compensated participant: " + this.getClass().toString());
+ getTaxiView().updateFields();
}
public String status () throws SystemException
@@ -140,13 +168,11 @@
*/
protected String txID;
- /**
- * The TaxiView object to log events through.
- */
- protected static TaxiView taxiView;
+ public TaxiView getTaxiView() {
+ return TaxiView.getSingletonInstance();
+ }
- /**
- * The TaxiManager to perform business logic operations on.
- */
- protected static TaxiManager taxiManager;
+ public TaxiManager getTaxiManager() {
+ return TaxiManager.getSingletonInstance();
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiServiceBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiServiceBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiServiceBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -93,18 +93,19 @@
BAParticipantManager participantManager = null;
try
{
- participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(taxiParticipant, new Uid().toString());
+ participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(taxiParticipant, "com.arjuna.xts-demorpc:taxiBA:" + new Uid().toString());
}
catch (Exception e)
{
taxiView.addMessage("id:" + transactionId + ". Participant enrolement failed");
+ taxiManager.cancelTaxi(transactionId);
System.err.println("bookTaxi: Participant enrolment failed");
e.printStackTrace(System.err);
return false;
}
- // finish the booking in the backend:
- taxiManager.commitTaxi(transactionId);
+ // finish the booking in the backend ensuring it is compensatable:
+ taxiManager.commitTaxi(transactionId, true);
try
{
@@ -114,6 +115,7 @@
catch (Exception e)
{
System.err.println("bookTaxi: 'completed' callback failed");
+ taxiManager.cancelTaxi(transactionId);
e.printStackTrace(System.err);
return false;
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreManager.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreManager.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -29,6 +29,8 @@
package com.arjuna.xts.nightout.services.Theatre;
+import com.arjuna.wst.FaultedException;
+
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.*;
@@ -47,8 +49,15 @@
*
* <li>nTotal[area] == nFree[area] + nPrepared[area] + nCommitted[area]
* </ul>
- * changes to nPrepared, nFree, nCommitted, nTotal and preparedList are always shadowed in
- * persistent storage before returning control to clients.
+ * Extended to include support for BA compensation based rollback
+ * </p>
+ * The manager now maintains an extra list compensatableList:
+ * <ul>
+ * <li>nCompensatable[area] == sum(compensatableList.seatCount[area])
+ * </ul>
+ * changes to nPrepared, nFree, nCommitted, nCompensatable, nTotal, preparedList and compensatableList are
+ * always shadowed in persistent storage before returning control to clients.
+ * </p>
*
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.4 $
@@ -231,6 +240,67 @@
}
/**
+ * Compensate a booking.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public boolean compensateSeats(Object txID)
+ throws FaultedException
+ {
+ boolean success = false;
+
+ // the transaction must be compensatable
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // see if the user wants to report a compensation fault
+
+ if (!autoCommitMode)
+ {
+ try
+ {
+ // wait for a user commit/rollback decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ if (!isCommit)
+ {
+ throw new FaultedException("TheatreManager.compensateSeats(): compensation fault");
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println("TheatreManager.compensateSeats(): Unexpected error during compensation.");
+ throw new FaultedException("TheatreManager.compensateSeats(): compensation fault");
+ }
+ }
+
+ // compensate the prepared transaction
+ Integer[] requests = (Integer[]) compensatableTransactions.remove(txID);
+ for (int i = 0; i < NUM_SEAT_AREAS; i++)
+ {
+ nCompensatableSeats[i] -= requests[i].intValue();
+ nCommittedSeats[i] -= requests[i].intValue();
+ nFreeSeats[i] += requests[i].intValue();
+ }
+ updateState();
+ success = true;
+ }
+ else
+ {
+ success = false; // error: transaction not registered
+ }
+
+ return success;
+ }
+
+ /**
* Commit seat bookings.
*
* @param txID The transaction identifier
@@ -238,6 +308,18 @@
*/
public boolean commitSeats(Object txID)
{
+ return commitSeats(txID, false);
+ }
+
+ /**
+ * Commit seat bookings, possibly allowing subsequent compensation.
+ *
+ * @param txID The transaction identifier
+ * @param compensatable true if it may be necessary to compensate this commit laer
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean commitSeats(Object txID, boolean compensatable)
+ {
boolean success = false;
// the transaction may be prepared, unprepared or unknown
@@ -247,8 +329,15 @@
// complete the prepared transaction
Integer[] requests = (Integer[]) preparedTransactions.remove(txID);
+ if (compensatable)
+ {
+ compensatableTransactions.put(txID, requests);
+ }
for (int i = 0; i < NUM_SEAT_AREAS; i++)
{
+ if (compensatable) {
+ nCompensatableSeats[i] += requests[i].intValue();
+ }
nCommittedSeats[i] += requests[i].intValue();
nPreparedSeats[i] -= requests[i].intValue();
nBookedSeats[i] -= requests[i].intValue();
@@ -260,18 +349,96 @@
{
// use one phase commit optimisation, skipping prepare
Integer[] requests = (Integer[]) unpreparedTransactions.remove(txID);
- for (int i = 0; i < NUM_SEAT_AREAS; i++)
+ boolean doCommit = true;
+ // check we have enough seats and if so
+ // use one phase commit optimisation, skipping prepare
+
+ if (autoCommitMode)
{
- nCommittedSeats[i] += requests[i].intValue();
- nFreeSeats[i] -= requests[i].intValue();
- nBookedSeats[i] -= requests[i].intValue();
+ for (int i = 0; doCommit && i < NUM_SEAT_AREAS; i++)
+ {
+ if (requests[i].intValue() > nFreeSeats[i])
+ {
+ doCommit = false;
+ }
+ }
}
+ else
+ {
+ try
+ {
+ // wait for a user decision
+ isPreparationWaiting = true;
+ synchronized (preparation)
+ {
+ preparation.wait();
+ }
+ isPreparationWaiting = false;
+
+ // process the user decision
+ doCommit = isCommit;
+ } catch (Exception e) {
+ System.err.println("TheatreManager.commitSeats(): Unable to perform commit.");
+ doCommit = false;
+ }
+ }
+
+ if (doCommit) {
+ if (compensatable) {
+ compensatableTransactions.put(txID, requests);
+ }
+ for (int i = 0; i < NUM_SEAT_AREAS; i++)
+ {
+ if (compensatable) {
+ nCompensatableSeats[i] += requests[i].intValue();
+ }
+ nCommittedSeats[i] += requests[i].intValue();
+ nFreeSeats[i] -= requests[i].intValue();
+ nBookedSeats[i] -= requests[i].intValue();
+ }
+ updateState();
+ success = true;
+ } else {
+ // get rid of the commitment to keep these seats
+ for (int i = 0; i < NUM_SEAT_AREAS; i++)
+ {
+ nBookedSeats[i] -= requests[i].intValue();
+ }
+ success = false;
+ }
+ }
+ else
+ {
+ success = false; // error: transaction not registered
+ }
+
+ return success;
+ }
+ /**
+ * Close seat bookings, removing possibility for compensation.
+ *
+ * @param txID The transaction identifier
+ * @return true on success, false otherwise
+ */
+ public synchronized boolean closeSeats(Object txID)
+ {
+ boolean success;
+
+ // the transaction may be compensatable or unknown
+
+ if (compensatableTransactions.containsKey(txID))
+ {
+ // complete the prepared transaction
+ Integer[] requests = (Integer[]) compensatableTransactions.remove(txID);
+ for (int i = 0; i < NUM_SEAT_AREAS; i++) {
+ nCompensatableSeats[i] -= requests[i].intValue();
+ }
updateState();
success = true;
}
else
{
- success = false; // error: transaction not registered
+ success = false; // error: transaction not registered for compensation
}
return success;
@@ -357,6 +524,16 @@
}
/**
+ * Get the number of compensatable seats in the given area.
+ *
+ * @return The number of compensatable seats
+ */
+ public int getNCompensatableSeats(int area)
+ {
+ return nCompensatableSeats[area];
+ }
+
+ /**
* Determine the autoCommit status of the instance.
*
* @return true if autoCommit mode is active, false otherwise
@@ -434,6 +611,7 @@
nBookedSeats = new int[NUM_SEAT_AREAS];
nPreparedSeats = new int[NUM_SEAT_AREAS];
nCommittedSeats = new int[NUM_SEAT_AREAS];
+ nCompensatableSeats = new int[NUM_SEAT_AREAS];
for (int i = 0; i < NUM_SEAT_AREAS; i++)
{
nTotalSeats[i] = DEFAULT_SEATING_CAPACITY;
@@ -441,7 +619,9 @@
nBookedSeats[i] = 0;
nPreparedSeats[i] = 0;
nCommittedSeats[i] = 0;
+ nCompensatableSeats[i] = 0;
}
+ compensatableTransactions = new Hashtable();
preparedTransactions = new Hashtable();
unpreparedTransactions = new Hashtable();
autoCommitMode = true;
@@ -506,6 +686,11 @@
private int[] nCommittedSeats;
/**
+ * The number of compensatable seats in each area.
+ */
+ private int[] nCompensatableSeats;
+
+ /**
* The auto commit mode.
* <p/>
* true = automatically commit, false = manually commit
@@ -538,6 +723,11 @@
private Hashtable preparedTransactions;
/**
+ * The transactions we know about and are prepared to compensate.
+ */
+ private Hashtable compensatableTransactions;
+
+ /**
* Constant (array index) used for the seating area CIRCLE.
*/
public static final int CIRCLE = 0;
@@ -648,8 +838,9 @@
nFreeSeats[i] = ois.readInt();
nPreparedSeats[i] = ois.readInt();
nCommittedSeats[i] = ois.readInt();
+ nCompensatableSeats[i] = ois.readInt();
}
- preparedTransactions = new Hashtable();
+ compensatableTransactions = new Hashtable();
String name = (String)ois.readObject();
while (!"".equals(name)) {
Integer[] counts = new Integer[NUM_SEAT_AREAS];
@@ -657,6 +848,17 @@
int count = ois.readInt();
counts[i] = new Integer(count);
}
+ compensatableTransactions.put(name, counts);
+ name = (String)ois.readObject();
+ }
+ preparedTransactions = new Hashtable();
+ name = (String)ois.readObject();
+ while (!"".equals(name)) {
+ Integer[] counts = new Integer[NUM_SEAT_AREAS];
+ for (int i = 0; i < NUM_SEAT_AREAS; i++) {
+ int count = ois.readInt();
+ counts[i] = new Integer(count);
+ }
preparedTransactions.put(name, counts);
name = (String)ois.readObject();
}
@@ -683,10 +885,21 @@
oos.writeInt(nFreeSeats[i]);
oos.writeInt(nPreparedSeats[i]);
oos.writeInt(nCommittedSeats[i]);
+ oos.writeInt(nCompensatableSeats[i]);
}
- Enumeration keys = preparedTransactions.keys();
+ Enumeration keys = compensatableTransactions.keys();
while (keys.hasMoreElements()) {
String name = (String)keys.nextElement();
+ Integer[] counts = (Integer[]) compensatableTransactions.get(name);
+ oos.writeObject(name);
+ for (int i = 0; i < NUM_SEAT_AREAS; i++) {
+ oos.writeInt(counts[i].intValue());
+ }
+ }
+ oos.writeObject("");
+ keys = preparedTransactions.keys();
+ while (keys.hasMoreElements()) {
+ String name = (String)keys.nextElement();
Integer[] counts = (Integer[]) preparedTransactions.get(name);
oos.writeObject(name);
for (int i = 0; i < NUM_SEAT_AREAS; i++) {
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreParticipantBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreParticipantBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreParticipantBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -30,8 +30,9 @@
package com.arjuna.xts.nightout.services.Theatre;
import com.arjuna.wst.*;
-import com.arjuna.ats.arjuna.common.Uid;
+import java.io.Serializable;
+
/**
* An adapter class that exposes the TheatreManager transaction lifecycle
* API as a WS-T Business Activity participant.
@@ -40,7 +41,7 @@
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
*/
-public class TheatreParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant
+public class TheatreParticipantBA implements BusinessAgreementWithParticipantCompletionParticipant, Serializable
{
/**
* Participant instances are related to business method calls
@@ -52,9 +53,6 @@
*/
public TheatreParticipantBA(String txID, int how_many, int which_area)
{
- // Binds to the singleton TheatreView and TheatreManager
- theatreManager = TheatreManager.getSingletonInstance();
- theatreView = TheatreView.getSingletonInstance();
// we need to save the txID for later use when logging
this.txID = txID;
// we also need the business paramater(s) in case of compensation
@@ -72,12 +70,19 @@
public void close() throws WrongStateException, SystemException
{
- // for logging only. This impl does not do anything else here.
+ // let the manager know that this activity no longer requires the option of compensation
System.out.println("TheatreParticipantBA.close");
- theatreView.addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
- theatreView.updateFields();
+ if (!getTheatreManager().closeSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("TheatreParticipantBA.close : not expecting a close for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected close for BA participant " + txID);
+ }
+
+ getTheatreView().addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
+ getTheatreView().updateFields();
}
/**
@@ -91,12 +96,19 @@
public void cancel() throws WrongStateException, SystemException
{
- // we will always have called completed or error, so this can be a null op.
+ // let the manager know that this activity has been cancelled
System.out.println("TheatreParticipantBA.cancel");
- theatreView.addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
- theatreView.updateFields();
+ if (!getTheatreManager().cancelSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("TheatreParticipantBA.cancel : not expecting a cancel for BA participant " + txID);
+
+ throw new WrongStateException("Unexpected cancel for BA participant " + txID);
+ }
+
+ getTheatreView().addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
+ getTheatreView().updateFields();
}
/**
@@ -112,42 +124,29 @@
{
System.out.println("TheatreParticipantBA.compensate");
- // Log the event and perform a compensating transaction
- // on the backend business logic if needed.
+ getTheatreView().addPrepareMessage("id:" + txID + ". Attempting to compensate participant: " + this.getClass().toString());
- theatreView.addPrepareMessage("id:" + txID + ". Compensate called on participant: " + this.getClass().toString());
- theatreView.updateFields();
+ getTheatreView().updateFields();
- if (seatCount > 0)
- {
- String compensatingTxID = new Uid().toString();
- // use a negative number of seats to 'reverse' the previous booking
- // This technique (hack) prevents us needing new business logic to support compensation.
- theatreManager.bookSeats(compensatingTxID, seatCount * -1, seatingArea);
- theatreView.updateFields();
+ // tell the manager to compensate
- boolean success = false;
- if(theatreManager.prepareSeats(compensatingTxID))
- {
- if (theatreManager.commitSeats(compensatingTxID))
- {
- theatreView.addMessage("id:" + txID + " Compensating transaction completed sucessfully.");
- theatreView.updateFields();
- success = true;
- }
- }
- else
- {
- theatreManager.cancelSeats(compensatingTxID);
- }
+ try {
+ if (!getTheatreManager().compensateSeats(txID)) {
+ // throw a WrongStateException to indicate that we were not expecting a close
+ System.out.println("TheatreParticipantBA.compensate : not expecting a compensate for BA participant " + txID);
- if(!success)
- {
- theatreView.addMessage("id:" + txID + " Compensation failed. Throwing FaultedException\n");
- theatreView.updateFields();
- throw new FaultedException("Compensating transaction failed.");
+ throw new WrongStateException("Unexpected compensate for BA participant " + txID);
}
+ } catch (FaultedException fe) {
+ getTheatreView().addMessage("id:" + txID + ". FaultedException when compensating participant: " + this.getClass().toString());
+
+ getTheatreView().updateFields();
+ throw fe;
}
+
+ getTheatreView().addMessage("id:" + txID + ". Compensated participant: " + this.getClass().toString());
+
+ getTheatreView().updateFields();
}
public String status()
@@ -182,13 +181,11 @@
*/
protected int seatingArea;
- /**
- * The TheatreView object to log events through.
- */
- protected static TheatreView theatreView;
+ public TheatreView getTheatreView() {
+ return TheatreView.getSingletonInstance();
+ }
- /**
- * The TheatreManager to perform business logic operations on.
- */
- protected static TheatreManager theatreManager;
+ public TheatreManager getTheatreManager() {
+ return TheatreManager.getSingletonInstance();
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreServiceBA.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreServiceBA.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreServiceBA.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -90,17 +90,19 @@
com.arjuna.wst.BAParticipantManager participantManager = null;
try
{
- participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(theatreParticipant, new Uid().toString());
+ participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(theatreParticipant, "com.arjuna.xts-demorpc:theatreBA:" + new Uid().toString());
}
catch (Exception e)
{
theatreView.addMessage("id:" + transactionId + ". Participant enrolement failed");
+ theatreManager.cancelSeats(transactionId);
System.err.println("bookSeats: Participant enrolement failed");
e.printStackTrace(System.err);
return false;
}
- theatreManager.commitSeats(transactionId);
+ // finish the booking in the backend ensuring it is compensatable:
+ theatreManager.commitSeats(transactionId, true);
try
{
@@ -109,6 +111,7 @@
catch (Exception e)
{
System.err.println("bookSeats: 'completed' callback failed");
+ theatreManager.cancelSeats(transactionId);
e.printStackTrace(System.err);
return false;
}
Copied: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCBARecoveryListener.java (from rev 23805, labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCATRecoveryListener.java)
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCBARecoveryListener.java (rev 0)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCBARecoveryListener.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,21 @@
+package com.arjuna.xts.nightout.services.recovery;
+
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletContextEvent;
+
+/**
+ * Listener to register and unregister teh XTS application specific listener -- we have to
+ * use this because JBossWS does not currently honour the @PostConstruct and @PreDestroy
+ * lifecycle annotations on web services
+ */
+public class DemoRPCBARecoveryListener implements ServletContextListener
+{
+
+ public void contextInitialized(ServletContextEvent event) {
+ DemoRPCBARecoveryModule.register();
+ }
+
+ public void contextDestroyed(ServletContextEvent event) {
+ DemoRPCBARecoveryModule.unregister();
+ }
+}
\ No newline at end of file
Property changes on: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCBARecoveryListener.java
___________________________________________________________________
Name: svn:mergeinfo
+
Copied: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCBARecoveryModule.java (from rev 23805, labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCATRecoveryModule.java)
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCBARecoveryModule.java (rev 0)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCBARecoveryModule.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,152 @@
+package com.arjuna.xts.nightout.services.recovery;
+
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryModule;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
+
+import java.io.ObjectInputStream;
+
+import com.arjuna.wst.BusinessAgreementWithParticipantCompletionParticipant;
+import com.arjuna.wst.BusinessAgreementWithCoordinatorCompletionParticipant;
+
+/**
+ * Application-specific WS-AT participant recovery manager for demo application, This class
+ * is responsible for recreating application-specific durable participants from records
+ * logged at prepare time.
+ */
+public class DemoRPCBARecoveryModule implements XTSBARecoveryModule
+{
+ /**
+ * the singleton recovery module
+ */
+ private static DemoRPCBARecoveryModule theRecoveryModule = null;
+
+ /**
+ * a count of how many xts demo services are currently installed
+ */
+ private static int serviceCount = 0;
+
+ /**
+ * called during deployment of an xts-demo web service to ensure the recovery module for the
+ * demo is installed whenever any of the services is active
+ */
+ public static void register()
+ {
+ if (theRecoveryModule == null) {
+ theRecoveryModule = new DemoRPCBARecoveryModule();
+ }
+ if (serviceCount == 0) {
+ XTSBARecoveryManager.getRecoveryManager().registerRecoveryModule(theRecoveryModule);
+ }
+ serviceCount++;
+ }
+
+ /**
+ * called during undeployment of an xts-demo web service to ensure the recovery module for
+ * the demo is deinstalled once none of the services is active
+ */
+ public static void unregister()
+ {
+ if (serviceCount > 0) {
+ serviceCount--;
+ if (serviceCount == 0) {
+ XTSBARecoveryManager.getRecoveryManager().unregisterRecoveryModule(theRecoveryModule);
+ }
+ }
+ }
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and recreate the participant by deserializing
+ * it from the supplied object input stream. n.b. this is only appropriate in case the
+ * original was a ParticipantCompletion participant saved using serialization.
+ * @param id the id used when the participant was created
+ * @param stream a stream from which the application should deserialise the participant
+ * if it recognises that the id belongs to the module's application
+ * @return the deserialized ParticipantCompletion participant
+ * @throws Exception if an error occurs deserializing the ParticipantCompletion participant
+ */
+ public BusinessAgreementWithParticipantCompletionParticipant deserializeParticipantCompletionParticipant(String id, ObjectInputStream stream) throws Exception {
+ if (id.startsWith("com.arjuna.xts-demorpc:restaurantBA") ||
+ id.startsWith("com.arjuna.xts-demorpc:theatreBA") ||
+ id.startsWith("com.arjuna.xts-demorpc:taxiBA")) {
+ System.out.println("xts-demorpc : attempting to deserialize WS-BA ParticipantCompletion participant " + id);
+ BusinessAgreementWithParticipantCompletionParticipant participant = (BusinessAgreementWithParticipantCompletionParticipant)stream.readObject();
+ System.out.println("xts-demorpc : deserialized WS-BA ParticipantCompletion participant " + id);
+ return participant;
+ }
+
+ return null;
+ }
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and use the saved recovery state to recreate the
+ * participant. n.b. this is only appropriate in case the original was a ParticipantCompletion
+ * participant saved after being converted to a byte array using the PersistibleBAParticipant
+ * interface.
+ * @param id the id used when the participant was created
+ * @param recoveryState a byte array returned form the original participant via a call to
+ * method getRecoveryState of interface PersistableBAParticipant
+ * @return the recreated ParticipantCompletion participant
+ * @throws Exception if an error occurs converting the recoveryState back to a
+ * ParticipantCompletion participant
+ */
+ public BusinessAgreementWithParticipantCompletionParticipant recreateParticipantCompletionParticipant(String id, byte[] recoveryState) throws Exception {
+ if (id.startsWith("com.arjuna.xts-demorpc:restaurantBA") ||
+ id.startsWith("com.arjuna.xts-demorpc:theatreBA") ||
+ id.startsWith("com.arjuna.xts-demorpc:taxiBA")) {
+ // this should not get called -- xts-demo WS-AT participants are saved and restored
+ // using serialization
+ throw new Exception("xts-demorpc : invalid request to recreate() WS-BA ParticipantCompletion participant " + id);
+ }
+ return null;
+ }
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and recreate the participant by deserializing
+ * it from the supplied object input stream. n.b. this is only appropriate in case the
+ * original was a CoordinatorCompletion participant saved using serialization.
+ * @param id the id used when the participant was created
+ * @param stream a stream from which the application should deserialise the participant
+ * if it recognises that the id belongs to the module's application
+ * @return the deserialized ParticipantCompletion participant
+ * @throws Exception if an error occurs deserializing the CoordinatorCompletion participant
+ */
+ public BusinessAgreementWithCoordinatorCompletionParticipant deserializeCoordinatorCompletionParticipant(String id, ObjectInputStream stream) throws Exception {
+ if (id.startsWith("com.arjuna.xts-demorpc:restaurantBA") ||
+ id.startsWith("com.arjuna.xts-demorpc:theatreBA") ||
+ id.startsWith("com.arjuna.xts-demorpc:taxiBA")) {
+ System.out.println("xts-demorpc : attempting to deserialize WS-BA CoordinatorCompletion participant " + id);
+ BusinessAgreementWithCoordinatorCompletionParticipant participant = (BusinessAgreementWithCoordinatorCompletionParticipant)stream.readObject();
+ System.out.println("xts-demorpc : deserialized WS-BA CoordinatorCompletion participant " + id);
+ return participant;
+ }
+
+ return null;
+ }
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and use the saved recovery state to recreate the
+ * participant. n.b. this is only appropriate in case the original was a CoordinatorCompletion
+ * participant saved after being converted to a byte array using the PersistibleBAParticipant
+ * interface.
+ * @param id the id used when the participant was created
+ * @param recoveryState a byte array returned form the original participant via a call to
+ * method getRecoveryState of interface PersistableBAParticipant
+ * @return the recreated ParticipantCompletion participant
+ * @throws Exception if an error occurs converting the recoveryState back to a
+ * CoordinatorCompletion participant
+ */
+ public BusinessAgreementWithCoordinatorCompletionParticipant recreateCoordinatorCompletionParticipant(String id, byte[] recoveryState) throws Exception {
+ if (id.startsWith("com.arjuna.xts-demorpc:restaurantBA") ||
+ id.startsWith("com.arjuna.xts-demorpc:theatreBA") ||
+ id.startsWith("com.arjuna.xts-demorpc:taxiBA")) {
+ // this should not get called -- xts-demo WS-AT participants are saved and restored
+ // using serialization
+ throw new Exception("xts-demorpc : invalid request to recreate() WS-BA CoordinatorCompletion participant " + id);
+ }
+ return null;
+ }
+}
\ No newline at end of file
Property changes on: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCBARecoveryModule.java
___________________________________________________________________
Name: svn:mergeinfo
+
Modified: labs/jbosstm/trunk/XTS/docs/XTSBARecoveryNotes.odt
===================================================================
(Binary files differ)
Modified: labs/jbosstm/trunk/XTS/docs/XTSBARecoveryNotes.pdf
===================================================================
(Binary files differ)
Modified: labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/XTSService.java
===================================================================
--- labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/XTSService.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/XTSService.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -22,7 +22,9 @@
import org.jboss.logging.Logger;
import org.jboss.jbossts.xts.recovery.coordinator.at.ACCoordinatorRecoveryModule;
+import org.jboss.jbossts.xts.recovery.coordinator.ba.BACoordinatorRecoveryModule;
import org.jboss.jbossts.xts.recovery.participant.at.ATParticipantRecoveryModule;
+import org.jboss.jbossts.xts.recovery.participant.ba.BAParticipantRecoveryModule;
//import com.arjuna.mw.wst.deploy.WSTXInitialisation;
//import com.arjuna.mw.wst.UserTransaction;
@@ -107,6 +109,9 @@
private ACCoordinatorRecoveryModule acCoordinatorRecoveryModule = null;
private ATParticipantRecoveryModule atParticipantRecoveryModule = null;
+ private BACoordinatorRecoveryModule baCoordinatorRecoveryModule = null;
+ private BAParticipantRecoveryModule baParticipantRecoveryModule = null;
+
// TODO: how to use a (per application) remote coordinator?
// does the http servlet param indicate its own location and the
// coordinatorURL indicate the coord??
@@ -234,28 +239,66 @@
atParticipantRecoveryModule.install();
+ // ok, now the recovery mocules for the BA coordinator and participant
+
+ baCoordinatorRecoveryModule = new BACoordinatorRecoveryModule();
+
+ // ensure Implementations are installed into the inventory before we register the module
+
+ baCoordinatorRecoveryModule.install();
+
+ // we don't need to install anything in the Inventory for this recovery module as it
+ // manages its own ObjectStore records but we do need it to create the recovery manager
+ // singleton.
+
+ baParticipantRecoveryModule = new BAParticipantRecoveryModule();
+
+ baParticipantRecoveryModule.install();
+
// we assume the tx manager has started, hence initializing the recovery manager.
// to guarantee this our mbean should depend on the tx mgr mbean. (but does that g/tee start or just load?)
+
+ // recovery should perform better if we register partiicpants first since this allows the XTS client
+ // recovery module to have a try at recreating the participant before its coordinator attempts to
+ // talk to it when they are both in the same VM. it also means the participant nay attempt bottom-up
+ // recovery before the coordinator is ready but coordinator recoveyr is probably going to happen quicker.
+
RecoveryManager.manager().addModule(atParticipantRecoveryModule);
+ RecoveryManager.manager().addModule(baParticipantRecoveryModule);
+
RecoveryManager.manager().addModule(acCoordinatorRecoveryModule);
+ RecoveryManager.manager().addModule(baCoordinatorRecoveryModule);
}
public void stop() throws Exception
{
log.info("JBossTS XTS Transaction Service - stopping");
+ if (baCoordinatorRecoveryModule != null) {
+ // remove the module, making sure any scan which might be using it has completed
+ RecoveryManager.manager().removeModule(baCoordinatorRecoveryModule, true);
+ // ok, now it is safe to get the recovery manager to uninstall its Implementations from the inventory
+ baCoordinatorRecoveryModule.uninstall();
+ }
if (acCoordinatorRecoveryModule != null) {
// remove the module, making sure any scan which might be using it has completed
RecoveryManager.manager().removeModule(acCoordinatorRecoveryModule, true);
// ok, now it is safe to get the recovery manager to uninstall its Implementations from the inventory
acCoordinatorRecoveryModule.uninstall();
}
+ if (baParticipantRecoveryModule != null) {
+ // remove the module, making sure any scan which might be using it has completed
+ RecoveryManager.manager().removeModule(baParticipantRecoveryModule, true);
+ // call uninstall even though it is currently a null op for this module
+ baParticipantRecoveryModule.uninstall();
+ }
if (atParticipantRecoveryModule != null) {
// remove the module, making sure any scan which might be using it has completed
RecoveryManager.manager().removeModule(atParticipantRecoveryModule, true);
// call uninstall even though it is currently a null op for this module
atParticipantRecoveryModule.uninstall();
}
+
TaskManager.getManager().shutdown() ; // com.arjuna.services.framework.admin.TaskManagerInitialisation
/*
Modified: labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/ACCoordinatorRecoveryModule.java
===================================================================
--- labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/ACCoordinatorRecoveryModule.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/ACCoordinatorRecoveryModule.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -42,13 +42,9 @@
/**
* This class is a plug-in module for the recovery manager.
- * It is responsible for recovering failed XTS (ACCoordinator) transactions.
+ * It is responsible for recovering failed XTS AT (ACCoordinator) transactions.
+ * (instances of com.arjuna.mwlabs.wscf.model.twophase.arjunacore.ACCoordinator)
*
- * Responsible for recovering instances of XTS Transaction Coordinators
- * (com.arjuna.mwlabs.wscf.model.as.coordinator.arjunacore.ACCoordinator)
- * Modelled on com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule
- * TODO: refactor this and AtomicActionRecoveryModule to remove duplication?
- *
* $Id$
*
* @message org.jboss.transactions.xts.recovery.coordinator.at.ACCoordinatorRecoveryModule_1 [org.jboss.transactions.xts.recovery.coordinator.at.ACCoordinatorRecoveryModule_1] - RecoveryManagerStatusModule: Object store exception: {0}
Modified: labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/ParticipantRecordSetup.java
===================================================================
--- labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/ParticipantRecordSetup.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/ParticipantRecordSetup.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -68,8 +68,7 @@
public ClassName className ()
{
- return RecordType.typeToClassName(RecordType.USER_DEF_FIRST0);
- //ClassName("WSATParticipantRecord"); // TODO remove dupl with ParticipantRecord
+ return RecordType.typeToClassName(RecordType.XTS_WSAT_RECORD);
}
}
Modified: labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/RecoverACCoordinator.java
===================================================================
--- labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/RecoverACCoordinator.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/at/RecoverACCoordinator.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -12,7 +12,7 @@
/**
* This class is a plug-in module for the recovery manager.
- * It is responsible for recovering failed ACCoordinator transactions.
+ * It is responsible for recovering failed WSAT ACCoordinator transactions.
*
* @message org.jboss.jbossts.xts.recovery.coordinator.at.RecoverACCoordinator_1 [org.jboss.jbossts.xts.recovery.coordinator.at.RecoverACCoordinator_1] - RecoverACCoordinator.replayPhase2 recovering {0} ActionStatus is {1}
* @message org.jboss.jbossts.xts.recovery.coordinator.at.RecoverACCoordinator_2 [org.jboss.jbossts.xts.recovery.coordinator.at.RecoverACCoordinator_2] - RecoverACCoordinator.replayPhase2: Unexpected status: {0}
@@ -21,8 +21,6 @@
*/
public class RecoverACCoordinator extends ACCoordinator {
- // TODO: refactor RecoverAtomicAction so that this can subclass it to remove dupl?
-
/**
* Re-creates/activates an AtomicAction for the specified
* transaction Uid.
Added: labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/BACoordinatorRecoveryModule.java
===================================================================
--- labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/BACoordinatorRecoveryModule.java (rev 0)
+++ labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/BACoordinatorRecoveryModule.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,346 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2007, 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) 2007,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.xts.recovery.coordinator.ba;
+
+import org.jboss.jbossts.xts.logging.XTSLogger;
+import org.jboss.jbossts.xts.recovery.participant.ba.XTSBARecoveryManager;
+
+import com.arjuna.ats.arjuna.recovery.RecoveryModule;
+import com.arjuna.ats.arjuna.recovery.TransactionStatusConnectionManager;
+import com.arjuna.ats.arjuna.logging.FacilityCode;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.common.util.logging.DebugLevel;
+import com.arjuna.common.util.logging.VisibilityLevel;
+
+import com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ * This class is a plug-in module for the recovery manager.
+ * It is responsible for recovering failed XTS BA (ACCoordinator) transactions.
+ * (instances of com.arjuna.mwlabs.wscf.model..arjunacore.ACCoordinator)
+ *
+ * $Id$
+ *
+ * @message org.jboss.transactions.xts.recovery.coordinator.ba.BACoordinatorRecoveryModule_1 [org.jboss.transactions.xts.recovery.coordinator.ba.BACoordinatorRecoveryModule_1] - RecoveryManagerStatusModule: Object store exception: {0}
+ * @message org.jboss.transactions.xts.recovery.coordinator.ba.BACoordinatorRecoveryModule_2 [org.jboss.transactions.xts.recovery.coordinator.ba.BACoordinatorRecoveryModule_2] - failed to recover Transaction {0} {1}
+ * @message org.jboss.transactions.xts.recovery.coordinator.ba.BACoordinatorRecoveryModule_3 [org.jboss.transactions.xts.recovery.coordinator.ba.BACoordinatorRecoveryModule_3] - failed to access transaction store {0} {1}
+ */
+public class BACoordinatorRecoveryModule implements RecoveryModule
+{
+ public BACoordinatorRecoveryModule()
+ {
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug
+ ( DebugLevel.CONSTRUCTORS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY,
+ "BACoordinatorRecoveryModule created - default" );
+ }
+
+ if (_transactionStore == null)
+ {
+ _transactionStore = TxControl.getStore() ;
+ }
+
+ _transactionStatusConnectionMgr = new TransactionStatusConnectionManager() ;
+ }
+
+ /**
+ * called by the service startup code before the recovery module is added to the recovery managers
+ * module list
+ */
+ public void install()
+ {
+ Implementations.install();
+ }
+
+ /**
+ * module list in order to allow the implementations list to be purged of this module's implementations
+ */
+ public void uninstall()
+ {
+ Implementations.uninstall();
+ }
+
+ /**
+ * This is called periodically by the RecoveryManager
+ */
+ public void periodicWorkFirstPass()
+ {
+ // Transaction type
+ boolean ACCoordinators = false ;
+
+ // uids per transaction type
+ InputObjectState acc_uids = new InputObjectState() ;
+
+ try
+ {
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug( "StatusModule: first pass " );
+ }
+
+ ACCoordinators = _transactionStore.allObjUids( _transactionType, acc_uids );
+
+ }
+ catch ( ObjectStoreException ex )
+ {
+ if (XTSLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.coordinator.ba.BACoordinatorRecoveryModule_1",
+ new Object[]{ex});
+ }
+ }
+
+ if ( ACCoordinators )
+ {
+ _transactionUidVector = processTransactions( acc_uids ) ;
+ }
+ }
+
+ public void periodicWorkSecondPass()
+ {
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug( "BACoordinatorRecoveryModule: Second pass " );
+ }
+
+ if (_transactionUidVector != null) {
+ processTransactionsStatus() ;
+ }
+
+ // ok notify the coordinator processor that recovery processing has completed
+
+ }
+
+ protected BACoordinatorRecoveryModule(String type)
+ {
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug
+ ( DebugLevel.CONSTRUCTORS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY,
+ "BACoordinatorRecoveryModule created " + type );
+ }
+
+ if (_transactionStore == null)
+ {
+ _transactionStore = TxControl.getStore() ;
+ }
+
+ _transactionStatusConnectionMgr = new TransactionStatusConnectionManager() ;
+ _transactionType = type;
+
+ }
+
+ private void doRecoverTransaction( Uid recoverUid )
+ {
+ boolean commitThisTransaction = true ;
+
+ // Retrieve the transaction status from its original process.
+ // n.b. for a non-active XTS TX this status wil l always be committed even
+ // if it aborted or had a heuristic outcome. in that case we need to use
+ // the logged action status which can only be retrieved after activation
+
+ int theStatus = _transactionStatusConnectionMgr.getTransactionStatus( _transactionType, recoverUid ) ;
+
+ boolean inFlight = isTransactionInMidFlight( theStatus ) ;
+
+ String Status = ActionStatus.stringForm( theStatus ) ;
+
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug
+ ( DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY,
+ "transaction type is "+ _transactionType + " uid is " +
+ recoverUid.toString() + "\n ActionStatus is " + Status +
+ " in flight is " + inFlight ) ;
+ }
+
+ if ( ! inFlight )
+ {
+ try
+ {
+ XTSLogger.arjLogger.debug( DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY, "jjh doing revovery here for "+recoverUid);
+ // TODO jjh
+ RecoverACCoordinator rcvACCoordinator =
+ new RecoverACCoordinator(recoverUid);
+// RecoverAtomicAction rcvAtomicAction =
+// new RecoverAtomicAction( recoverUid, theStatus ) ;
+
+// rcvAtomicAction.replayPhase2() ;
+ rcvACCoordinator.replayPhase2();
+ }
+ catch ( Exception ex )
+ {
+ if (XTSLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.coordinator.ba.BACoordinatorRecoveryModule_2",
+ new Object[]{recoverUid.toString(), ex});
+ }
+ }
+ }
+ }
+
+ private boolean isTransactionInMidFlight( int status )
+ {
+ boolean inFlight = false ;
+
+ switch ( status )
+ {
+ // these states can only come from a process that is still alive
+ case ActionStatus.RUNNING :
+ case ActionStatus.ABORT_ONLY :
+ case ActionStatus.PREPARING :
+ case ActionStatus.COMMITTING :
+ case ActionStatus.ABORTING :
+ case ActionStatus.PREPARED :
+ inFlight = true ;
+ break ;
+
+ // the transaction is apparently still there, but has completed its
+ // phase2. should be safe to redo it.
+ case ActionStatus.COMMITTED :
+ case ActionStatus.H_COMMIT :
+ case ActionStatus.H_MIXED :
+ case ActionStatus.H_HAZARD :
+ case ActionStatus.ABORTED :
+ case ActionStatus.H_ROLLBACK :
+ inFlight = false ;
+ break ;
+
+ // this shouldn't happen
+ case ActionStatus.INVALID :
+ default:
+ inFlight = false ;
+ }
+
+ return inFlight ;
+ }
+
+ private Vector processTransactions( InputObjectState uids )
+ {
+ Vector uidVector = new Vector() ;
+
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug( DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY,
+ "processing " + _transactionType
+ + " transactions" ) ;
+ }
+
+ Uid theUid = new Uid( Uid.nullUid() );
+
+ boolean moreUids = true ;
+
+ while (moreUids)
+ {
+ try
+ {
+ theUid.unpack( uids ) ;
+
+ if (theUid.equals( Uid.nullUid() ))
+ {
+ moreUids = false;
+ }
+ else
+ {
+ Uid newUid = new Uid( theUid ) ;
+
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug
+ ( DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY,
+ "found transaction "+ newUid ) ;
+ }
+
+ uidVector.addElement( newUid ) ;
+ }
+ }
+ catch ( Exception ex )
+ {
+ moreUids = false;
+ }
+ }
+ return uidVector ;
+ }
+
+ private void processTransactionsStatus()
+ {
+ // Process the Vector of transaction Uids
+ Enumeration transactionUidEnum = _transactionUidVector.elements() ;
+
+ while ( transactionUidEnum.hasMoreElements() )
+ {
+ Uid currentUid = (Uid) transactionUidEnum.nextElement();
+
+ try
+ {
+ if ( _transactionStore.currentState( currentUid, _transactionType ) != ObjectStore.OS_UNKNOWN )
+ {
+ doRecoverTransaction( currentUid ) ;
+ }
+ }
+ catch ( ObjectStoreException ex )
+ {
+ if (XTSLogger.arjLogger.isWarnEnabled())
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.coordinator.ba.BACoordinatorRecoveryModule_3",
+ new Object[]{currentUid.toString(), ex});
+ }
+ }
+ }
+
+ XTSBARecoveryManager.getRecoveryManager().setCoordinatorRecoveryStarted();
+ }
+
+ // 'type' within the Object Store for ACCoordinator.
+ private String _transactionType = new ACCoordinator().type() ;
+
+ // Array of transactions found in the object store of the
+ // ACCoordinator type.
+ private Vector _transactionUidVector = null ;
+
+ // Reference to the Object Store.
+ private static ObjectStore _transactionStore = null ;
+
+ // This object manages the interface to all TransactionStatusManagers
+ // processes(JVMs) on this system/node.
+ private TransactionStatusConnectionManager _transactionStatusConnectionMgr ;
+
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/Implementations.java
===================================================================
--- labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/Implementations.java (rev 0)
+++ labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/Implementations.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+package org.jboss.jbossts.xts.recovery.coordinator.ba;
+
+import com.arjuna.ats.arjuna.gandiva.inventory.Inventory;
+
+/**
+ * Module specific class that is responsible for adding any implementations
+ * to the inventory.
+ *
+ * @author Mark Little (mark at arjuna.com)
+ * @version $Id: Implementations.java 2342 2006-03-30 13:06:17Z $
+ * @since JTS 1.0.
+ */
+public class Implementations {
+
+ static ParticipantRecordSetup _inventoryItem = null;
+
+ public static synchronized void install ()
+ {
+ if (_inventoryItem == null) {
+ _inventoryItem = new ParticipantRecordSetup();
+ // WS-BA Participant records.
+ Inventory.inventory().addToList(_inventoryItem);
+ }
+ }
+
+ public static synchronized void uninstall()
+ {
+ if (_inventoryItem != null) {
+ Inventory.inventory().removeFromList(_inventoryItem.className());
+ }
+ }
+
+ private Implementations()
+ {
+ }
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/ParticipantRecordSetup.java
===================================================================
--- labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/ParticipantRecordSetup.java (rev 0)
+++ labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/ParticipantRecordSetup.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package org.jboss.jbossts.xts.recovery.coordinator.ba;
+
+import com.arjuna.ats.arjuna.gandiva.inventory.InventoryElement;
+import com.arjuna.ats.arjuna.gandiva.ClassName;
+import com.arjuna.ats.arjuna.gandiva.ObjectName;
+import com.arjuna.ats.arjuna.coordinator.RecordType;
+
+import com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ParticipantRecord;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jhalli
+ * Date: Aug 20, 2007
+ * Time: 3:18:23 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class ParticipantRecordSetup implements InventoryElement {
+
+ public synchronized Object createVoid ()
+ {
+ return ParticipantRecord.create();
+ }
+
+ public synchronized Object createClassName (ClassName className)
+ {
+ return null;
+ }
+
+ public synchronized Object createObjectName (ObjectName objectName)
+ {
+ return null;
+ }
+
+ public synchronized Object createResources (Object[] resources)
+ {
+ return null;
+ }
+
+ public synchronized Object createClassNameResources (ClassName className, Object[] resources)
+ {
+ return null;
+ }
+
+ public synchronized Object createObjectNameResources (ObjectName objectName, Object[] resources)
+ {
+ return null;
+ }
+
+ public ClassName className ()
+ {
+ return RecordType.typeToClassName(RecordType.XTS_WSBA_RECORD);
+ }
+
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/RecoverACCoordinator.java
===================================================================
--- labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/RecoverACCoordinator.java (rev 0)
+++ labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/coordinator/ba/RecoverACCoordinator.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,133 @@
+package org.jboss.jbossts.xts.recovery.coordinator.ba;
+
+import org.jboss.jbossts.xts.logging.XTSLogger;
+
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.logging.FacilityCode;
+import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.coordinator.RecordListIterator;
+import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
+
+import com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ACCoordinator;
+import com.arjuna.mwlabs.wscf.model.sagas.arjunacore.ParticipantRecord;
+import com.arjuna.common.util.logging.DebugLevel;
+import com.arjuna.common.util.logging.VisibilityLevel;
+import com.arjuna.mw.wsas.exceptions.SystemException;
+import com.arjuna.mw.wscf.logging.wscfLogger;
+
+/**
+ * This class is a plug-in module for the recovery manager.
+ * It is responsible for recovering failed WSBA ACCoordinator transactions.
+ *
+ * @message org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator_1 [org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator_1] - RecoverACCoordinator.replayPhase2 recovering {0} ActionStatus is {1}
+ * @message org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator_2 [org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator_2] - RecoverACCoordinator.replayPhase2: Unexpected status: {0}
+ * @message org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator_3 [org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator_3] - RecoverACCoordinator.replayPhase2( {0} ) finished
+ * @message org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator_4 [org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator_4] - RecoverACCoordinator.replayPhase2 transaction {0} not activated, unable to replay phase 2 commit
+*/
+public class RecoverACCoordinator extends ACCoordinator {
+
+ /**
+ * Re-creates/activates an AtomicAction for the specified
+ * transaction Uid.
+ */
+ public RecoverACCoordinator( Uid rcvUid )
+ {
+ super( rcvUid ) ;
+ _activated = activate() ;
+ if (_activated) {
+ setRecoveryCoordinator();
+ }
+ }
+
+ /**
+ * provide the recovered participants with a handle on this coordinator so they can
+ * propagate events through to it.
+ */
+
+ public void setRecoveryCoordinator()
+ {
+ if (preparedList != null)
+ {
+ RecordListIterator iter = new RecordListIterator(preparedList);
+ AbstractRecord absRec = iter.iterate();
+
+ while (absRec != null)
+ {
+ if (absRec instanceof ParticipantRecord)
+ {
+ ParticipantRecord pr = (ParticipantRecord) absRec;
+ pr.setRecoveryCoordinator(this);
+ }
+
+ absRec = iter.iterate();
+ }
+ }
+ }
+
+ /**
+ * Replays phase 2 of the commit protocol.
+ */
+ public void replayPhase2()
+ {
+ final int status = status();
+
+ if (XTSLogger.arjLoggerI18N.debugAllowed())
+ {
+ XTSLogger.arjLoggerI18N.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY,
+ "org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator_1",
+ new Object[]{get_uid(), ActionStatus.stringForm(status)});
+ }
+
+ if ( _activated )
+ {
+ // we only need to rerun phase2 if the action status is PREPARED, which happens
+ // when we crash between a successful complete beginning a close, or COMMITTING, which
+ // happens when we get a comms timeout from one of the participants after sending it a CLOSE
+ // message. in the former case all participant records will be listed in the prepared list.
+ // in the latter case the failed participant record(s) will have been reinstated in the
+ // prepared list and the participant stub engine reactivated, where necessary,
+ // under the call to activate() when this coordinator was created.
+
+ // we can also arrive here when the action status is ABORTING. This happens when we
+ // get a comms timeout from one of the participants after sending it a CANCEL message
+ // or if we get a comms timeout from one of the participants after sending it a COMPENSATE
+ // message.
+
+ if ((status == ActionStatus.PREPARED) ||
+ (status == ActionStatus.COMMITTING) ||
+ (status == ActionStatus.COMMITTED) ||
+ (status == ActionStatus.H_COMMIT) ||
+ (status == ActionStatus.H_MIXED) ||
+ (status == ActionStatus.H_HAZARD))
+ {
+ super.phase2Commit( _reportHeuristics ) ;
+ } else if ((status == ActionStatus.ABORTED) ||
+ (status == ActionStatus.H_ROLLBACK) ||
+ (status == ActionStatus.ABORTING) ||
+ (status == ActionStatus.ABORT_ONLY))
+ {
+ super.phase2Abort( _reportHeuristics ) ;
+ }
+
+ if (XTSLogger.arjLoggerI18N.debugAllowed())
+ {
+ XTSLogger.arjLoggerI18N.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY,
+ "org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator_3",
+ new Object[]{get_uid()});
+ }
+ }
+ else
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator_4", new Object[]{get_uid()});
+ }
+ }
+
+ // Flag to indicate that this transaction has been re-activated
+ // successfully.
+ private boolean _activated = false ;
+
+ // whether heuristic reporting on phase 2 commit is enabled.
+ private boolean _reportHeuristics = true ;
+}
\ No newline at end of file
Modified: labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/at/ATParticipantRecoveryModule.java
===================================================================
--- labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/at/ATParticipantRecoveryModule.java 2008-11-12 10:01:31 UTC (rev 23834)
+++ labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/at/ATParticipantRecoveryModule.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -41,13 +41,9 @@
/**
* This class is a plug-in module for the recovery manager.
- * It is responsible for recovering failed XTS (ACCoordinator) transactions.
+ * It is responsible for recovering XTS AT participants.
+ * (instances of org.jboss.jbossts.xts.recovery.participant.at.ATParticipantRecoveryRecord)
*
- * Responsible for recovering instances of XTS Transaction Coordinators
- * (com.arjuna.mwlabs.wscf.model.as.coordinator.arjunacore.ACCoordinator)
- * Modelled on com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule
- * TODO: refactor this and AtomicActionRecoveryModule to remove duplication?
- *
* $Id$
*
* @message org.jboss.transactions.xts.recovery.participant.at.ATParticipantRecoveryModule_1 [org.jboss.transactions.xts.recovery.participant.at.ATParticipantRecoveryModule_1] - RecoveryManagerStatusModule: Object store exception: {0}
Added: labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantRecoveryModule.java
===================================================================
--- labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantRecoveryModule.java (rev 0)
+++ labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/ba/BAParticipantRecoveryModule.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,327 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2007, 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) 2007,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.xts.recovery.participant.ba;
+
+import org.jboss.jbossts.xts.logging.XTSLogger;
+
+import com.arjuna.ats.arjuna.recovery.RecoveryModule;
+import com.arjuna.ats.arjuna.logging.FacilityCode;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.common.util.logging.DebugLevel;
+import com.arjuna.common.util.logging.VisibilityLevel;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.io.IOException;
+
+/**
+ * This class is a plug-in module for the recovery manager.
+ * It is responsible for recovering XTS BA participants
+ * (instances of org.jboss.jbossts.xts.recovery.participant.ba.BAParticipantRecoveryRecord)
+ *
+ * $Id$
+ *
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_1 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_1] - RecoveryManagerStatusModule: Object store exception: {0}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_3 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_3] - failed to access transaction store {0}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_4 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_4] - unable to load recovery record implementation class {0} for WS-BA participant recovery record {1}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_5 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_5] - unable to instantiate recovery record implementation class {0} for WS-BA participant recovery record {1}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_6 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_6] - unable to unpack recovery record data for WS-BA participant recovery record {0}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_7 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_7] - missing recovery record data for WS-BA participant recovery record {0}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_8 [org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_8] - unable to read recovery record data for WS-BA participant recovery record {0}
+ */
+
+public class BAParticipantRecoveryModule implements RecoveryModule
+{
+ public BAParticipantRecoveryModule()
+ {
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug
+ ( DebugLevel.CONSTRUCTORS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY,
+ "BAParticipantRecoveryModule created - default" );
+ }
+
+ if (_objectStore == null)
+ {
+ _objectStore = TxControl.getStore() ;
+ }
+
+ _participantType = BAParticipantRecoveryRecord.type();
+ }
+
+ /**
+ * called by the service startup code before the recovery module is added to the recovery managers
+ * module list
+ */
+ public void install()
+ {
+ XTSBARecoveryManager.setRecoveryManager(new XTSBARecoveryManagerImple(_objectStore));
+ }
+
+ /**
+ * called by the service shutdown code after the recovery module is removed from the recovery managers
+ * module list
+ */
+ public void uninstall()
+ {
+ }
+
+ /**
+ * This is called periodically by the RecoveryManager
+ */
+ public void periodicWorkFirstPass()
+ {
+ // Transaction type
+ boolean BAParticipants = false ;
+
+ // uids per transaction type
+ InputObjectState acc_uids = new InputObjectState() ;
+
+ try
+ {
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug( "StatusModule: first pass " );
+ }
+
+ BAParticipants = _objectStore.allObjUids(_participantType, acc_uids );
+
+ }
+ catch ( ObjectStoreException ex )
+ {
+ if (XTSLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_1",
+ ex);
+ }
+ }
+
+ if ( BAParticipants )
+ {
+ _participantUidVector = processParticipants( acc_uids ) ;
+ }
+ }
+
+ public void periodicWorkSecondPass()
+ {
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug( "BAParticipantRecoveryModule: Second pass " );
+ }
+
+ processParticipantsStatus() ;
+ }
+
+ private void doRecoverParticipant( Uid recoverUid )
+ {
+ // Retrieve the participant from its original process.
+
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug
+ ( DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY,
+ "participant type is "+ _participantType + " uid is " +
+ recoverUid.toString()) ;
+ }
+
+ // we don't need to use a lock here because we only attempt the read
+ // when the uid is inactive which means it cannto be pulled out form under our
+ // feet at commit. uniqueness of uids also means we can't be foiled by a reused
+ // uid.
+
+ XTSBARecoveryManager recoveryManager = XTSBARecoveryManager.getRecoveryManager();
+
+ if (!recoveryManager.isParticipantPresent(recoverUid)) {
+ // ok, the participant can neither be active nor loaded awaiting recreation by
+ // an application recovery module so we need to load it
+ try {
+ // retrieve the data for the participant
+ InputObjectState inputState = _objectStore.read_committed(recoverUid, _participantType);
+
+ if (inputState != null) {
+ try {
+ String participantRecordClazzName = inputState.unpackString();
+ try {
+ // create a participant engine instance and tell it to recover itself
+ Class participantRecordClazz = Class.forName(participantRecordClazzName);
+ BAParticipantRecoveryRecord participantRecord = (BAParticipantRecoveryRecord)participantRecordClazz.newInstance();
+ participantRecord.restoreState(inputState);
+ // ok, now insert into recovery map if needed
+ XTSBARecoveryManager.getRecoveryManager().addParticipantRecoveryRecord(recoverUid, participantRecord);
+ } catch (ClassNotFoundException cnfe) {
+ // oh boy, not supposed to happen -- n.b. either the user deployed 1.0
+ // last time and 1.1 this time or vice versa or something is rotten in
+ // the state of Danmark
+ if (XTSLogger.arjLoggerI18N.isErrorEnabled())
+ {
+ XTSLogger.arjLoggerI18N.error("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_4",
+ new Object[]{participantRecordClazzName, recoverUid.toString()}, cnfe);
+ }
+ } catch (InstantiationException ie) {
+ // this is also worrying, log an error
+ if (XTSLogger.arjLoggerI18N.isErrorEnabled())
+ {
+ XTSLogger.arjLoggerI18N.error("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_5",
+ new Object[]{participantRecordClazzName, recoverUid.toString()}, ie);
+ }
+ } catch (IllegalAccessException iae) {
+ // this is another configuration problem, log an error
+ if (XTSLogger.arjLoggerI18N.isErrorEnabled())
+ {
+ XTSLogger.arjLoggerI18N.error("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_5",
+ new Object[]{participantRecordClazzName, recoverUid.toString()}, iae);
+ }
+ }
+ } catch (IOException ioe) {
+ // hmm, record corrupted? log this as a warning
+ if (XTSLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_6",
+ new Object[]{recoverUid.toString()}, ioe);
+ }
+ }
+ } else {
+ // hmm, it ought not to be able to disappear unless the recovery manager knows about it
+ // this is an error!
+ if (XTSLogger.arjLoggerI18N.isErrorEnabled())
+ {
+ XTSLogger.arjLoggerI18N.error("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_7",
+ new Object[]{recoverUid.toString()});
+ }
+ }
+ } catch (ObjectStoreException ose) {
+ // if the object store is not working this is serious
+ if (XTSLogger.arjLoggerI18N.isErrorEnabled())
+ {
+ XTSLogger.arjLoggerI18N.error("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_8",
+ new Object[]{recoverUid.toString()}, ose);
+ }
+ }
+ }
+ }
+
+ private Vector processParticipants( InputObjectState uids )
+ {
+ Vector uidVector = new Vector() ;
+
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug( DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY,
+ "processing " + _participantType
+ + " WS-BA participants" ) ;
+ }
+
+ Uid theUid = new Uid( Uid.nullUid() );
+
+ boolean moreUids = true ;
+
+ while (moreUids)
+ {
+ try
+ {
+ theUid.unpack( uids ) ;
+
+ if (theUid.equals( Uid.nullUid() ))
+ {
+ moreUids = false;
+ }
+ else
+ {
+ Uid newUid = new Uid( theUid ) ;
+
+ if (XTSLogger.arjLogger.isDebugEnabled())
+ {
+ XTSLogger.arjLogger.debug
+ ( DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CRASH_RECOVERY,
+ "found WS-BA participant "+ newUid ) ;
+ }
+
+ uidVector.addElement( newUid ) ;
+ }
+ }
+ catch ( Exception ex )
+ {
+ moreUids = false;
+ }
+ }
+ return uidVector ;
+ }
+
+ private void processParticipantsStatus()
+ {
+ if (_participantUidVector != null) {
+ // Process the Vector of transaction Uids
+ Enumeration participantUidEnum = _participantUidVector.elements() ;
+
+ while ( participantUidEnum.hasMoreElements() )
+ {
+ Uid currentUid = (Uid) participantUidEnum.nextElement();
+
+ try
+ {
+ if ( _objectStore.currentState( currentUid, _participantType) != ObjectStore.OS_UNKNOWN )
+ {
+ doRecoverParticipant( currentUid ) ;
+ }
+ }
+ catch ( ObjectStoreException ex )
+ {
+ if (XTSLogger.arjLogger.isWarnEnabled())
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.BAParticipantRecoveryModule_3",
+ new Object[]{currentUid.toString()}, ex);
+ }
+ }
+ }
+ }
+
+ // now get the BA recovery manager to try to activate recovered participants
+
+ XTSBARecoveryManager.getRecoveryManager().recoverParticipants();
+ }
+
+ // 'type' within the Object Store for BAParticipant record.
+ private String _participantType = BAParticipantRecoveryRecord.type() ;
+
+ // Array of transactions found in the object store of the
+ // ACCoordinator type.
+ private Vector _participantUidVector = null ;
+
+ // Reference to the Object Store.
+ private static ObjectStore _objectStore = null ;
+
+ // This object provides information about whether or not a participant is currently active.
+
+ private HashMap<String, BAParticipantRecoveryRecord> _recoveredParticipantMap ;
+}
\ No newline at end of file
Added: labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryManagerImple.java
===================================================================
--- labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryManagerImple.java (rev 0)
+++ labs/jbosstm/trunk/XTS/sar/src/org/jboss/jbossts/xts/recovery/participant/ba/XTSBARecoveryManagerImple.java 2008-11-12 11:08:25 UTC (rev 23835)
@@ -0,0 +1,342 @@
+package org.jboss.jbossts.xts.recovery.participant.ba;
+
+import org.jboss.jbossts.xts.logging.XTSLogger;
+
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.ats.arjuna.state.OutputObjectState;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
+
+import java.util.*;
+import java.io.IOException;
+
+/**
+ * A class which manages the table of recovered participant records.
+ *
+ * @message org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_1 [org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_1] exception writing recovery record for WS-BA participant {0}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_2 [org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_2] exception removing recovery record {0} for WS-BA participant {1}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_3 [org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_3] exception reactivating recovered WS-BA participant {0}
+ * @message org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_4 [org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_4] no XTS application recovery module found to help reactivate recovered WS-BA participant {0}
+ */
+public class XTSBARecoveryManagerImple extends XTSBARecoveryManager {
+ /**
+ * constructor for use by BAParticipantRecoveryModule
+ * @param objectStore
+ */
+ XTSBARecoveryManagerImple(ObjectStore objectStore)
+ {
+ this.objectStore = objectStore;
+ }
+
+ /**
+ * register an application specific recovery module which acts as a helper to recreate
+ * a WS-BA durable participant from the participant's recovery data saved at prepare
+ *
+ * @param module the module which will be used to identify and recreate participants
+ * for the application
+ * @throws NullPointerException if the supplied module is null
+ */
+ public void registerRecoveryModule(XTSBARecoveryModule module) throws NullPointerException
+ {
+ // TODO other sanity checks?
+ if (module == null) {
+ throw new NullPointerException("XTSBARecoveryModule value must be non-null");
+ }
+
+ recoveryModules.add(module);
+ }
+
+ /**
+ * unregister an application specific recovery module previously registered as
+ * a helper to recretae WS-BA durable participants
+ *
+ * @param module the module to be unregistered
+ * @throws java.util.NoSuchElementException
+ * if the module is not currently registered
+ */
+ public void unregisterRecoveryModule(XTSBARecoveryModule module) throws NoSuchElementException {
+ if (!recoveryModules.remove(module)) {
+ throw new NoSuchElementException();
+ }
+ }
+
+ /**
+ * save the supplied participant recovery record to persistent storage
+ *
+ * @param participantRecoveryRecord
+ */
+ public boolean writeParticipantRecoveryRecord(BAParticipantRecoveryRecord participantRecoveryRecord)
+ {
+ OutputObjectState oos = new OutputObjectState();
+ // we need to be able to retrieve the class of the participant record so we can
+ // create an instancde to load the rest of the participant specific data
+ try {
+ oos.packString(participantRecoveryRecord.getClass().getCanonicalName());
+ } catch (IOException ioe) {
+ if (XTSLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_1",
+ new Object[] {participantRecoveryRecord.getId()}, ioe);
+ }
+ return false;
+ }
+
+ if (participantRecoveryRecord.saveState(oos)) {
+ Uid uid = new Uid();
+ try {
+ objectStore.write_committed(uid, type, oos);
+ // we need to be able to identify the uid from the participant id
+ // in order to delete it later
+ uidMap.put(participantRecoveryRecord.getId(), uid);
+ return true;
+ } catch (ObjectStoreException ose) {
+ if (XTSLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_1",
+ new Object[] {participantRecoveryRecord.getId()}, ose);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * remove any participant recovery record with the supplied id from persistent storage
+ * @param id the id of the participant whose recovery details are being deleted
+ */
+ public boolean deleteParticipantRecoveryRecord(String id)
+ {
+ Uid uid = uidMap.get(id);
+
+ if (uid != null) {
+
+ try {
+ objectStore.remove_committed(uid, type);
+ uidMap.remove(id);
+ return true;
+ } catch (ObjectStoreException ose) {
+ if (XTSLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_2",
+ new Object[] {uid, id},
+ ose);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * test whether the supplied uid identifies an active participant or a recovered but inactive
+ * participant
+ *
+ * n.b. this method is not synchronized because of two assumptions: first, that uids are
+ * never reused; and second that only recovery scanning (as a minimum, for a given recovery
+ * record type) is single-threaded. Correctness of the first assumption ensures there are no
+ * races with participant processor threads, the second races between recovery threads.
+ *
+ * @param uid
+ */
+ public boolean isParticipantPresent(Uid uid)
+ {
+ return (uidMap.get(uid) != null);
+ }
+
+ /**
+ * add a recovered participant record to the table of unrecovered participants which
+ * need to be recreated following recovery
+ *
+ * @param uid the uid under which the participant was saved in the file store
+ * @param participantRecoveryRecord the in-memory representation of the recovery record
+ * saved to disk
+ */
+ public void addParticipantRecoveryRecord(Uid uid, BAParticipantRecoveryRecord participantRecoveryRecord)
+ {
+ String participantId = participantRecoveryRecord.getId();
+ if (recoveryMap.get(participantId) == null && !participantRecoveryRecord.isActive()) {
+ // ok, we have not seen this entry before so add it to the list
+ recoveryMap.put(participantId, participantRecoveryRecord);
+ uidMap.put(participantId, uid);
+ }
+ }
+
+ /**
+ * see if a participant recovery record with a given id exists in the table of participants which
+ * need to be recreated following recovery
+ * @param id the identifier of the participant being sought
+ * @return the participant recovery record with the supplied id or null if it is not found
+ */
+ public synchronized BAParticipantRecoveryRecord findParticipantRecoveryRecord(String id)
+ {
+ return recoveryMap.get(id);
+ }
+
+ /**
+ * process all entries in the recovered participant map and attempt to recreate the
+ * application participant and activate it
+ */
+ public void recoverParticipants()
+ {
+ // the first scan has been performed so allow processing of commit and rollback requests
+ // for unknown ids to proceed now
+
+ setParticipantRecoveryStarted();
+
+ // we operate on a copy of the recovery modules to avoid the list being modified
+ // by register and unregister operations while we are iterating over it
+ // we should probably also make sure unregister does not proceed until
+ // the current scan is complete . . .
+
+ List<XTSBARecoveryModule> recoveryModulesCopy;
+ synchronized (recoveryModules) {
+ recoveryModulesCopy = new ArrayList<XTSBARecoveryModule>(recoveryModules);
+ }
+
+ // iterate through the participant recovery records and try to convert them to
+ // a durable participant. if successful activate the participant and then remove the
+ // recovery entry. note that since recovery is single threaded we can be sure that
+ // no concurrent modifications will be made to the table while we are iterating and,
+ // possibly, deleting via the iterator
+
+ Iterator<BAParticipantRecoveryRecord> participantIterator = iterator();
+
+ while(participantIterator.hasNext()) {
+ BAParticipantRecoveryRecord participantRecoveryRecord = participantIterator.next();
+ if (participantRecoveryRecord.isActive()) {
+ // this participant must have already been activated by a by a previous
+ // scan and been reloaded by this scan so just remove the entry
+
+ participantIterator.remove();
+ } else {
+ Iterator<XTSBARecoveryModule> moduleIterator = recoveryModulesCopy.iterator();
+ boolean found = false;
+
+ while (!found && moduleIterator.hasNext()) {
+ XTSBARecoveryModule module = moduleIterator.next();
+ try {
+ if (participantRecoveryRecord.restoreParticipant(module)) {
+ // ok, this participant has recovered so tell it to
+ // activate and *then* remove it from the hashmap. this makes
+ // sure we don't open a window where an incoming
+ // commit may fail to find the object in either table
+
+ found = true;
+ participantRecoveryRecord.activate();
+
+ participantIterator.remove();
+ }
+ } catch (Exception e) {
+ // we foudn a helper but it failed to convert the participant record -- log a warning
+ // but leave the participant in the table for next time in case the helper has merely
+ // suffered a transient failure
+ found = true;
+ if (XTSLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_3",
+ new Object[] {participantRecoveryRecord.getId()},
+ e);
+ }
+ }
+ }
+
+ if (!found) {
+ // we failed to find a helper to convert a participant record so log a warning
+ // but leave it in the table for next time
+ if (XTSLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ XTSLogger.arjLoggerI18N.warn("org.jboss.transactions.xts.recovery.participant.ba.XTSBARecoveryModule_4",
+ new Object[] {participantRecoveryRecord.getId()});
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * return an iterator over the collection of entries in the table. n.b. this iterates
+ * direct over the table so any deletions performed during iteration need to be done
+ * via the iterator and need to be sure to avoid concurrent modification
+ * @return
+ */
+ private synchronized Iterator<BAParticipantRecoveryRecord> iterator()
+ {
+ return recoveryMap.values().iterator();
+ }
+
+ /**
+ * set a global flag indicating that the first BA participant recovery scan has
+ * been performed.
+ */
+ private synchronized void setParticipantRecoveryStarted()
+ {
+ participantRecoveryStarted = true;
+ }
+
+ /**
+ * test whether the first BA participant recovery scan has completed. this indicates whether
+ * there may or may not still be unknown participant recovery records on disk. If the first
+ * scan has not yet completed then a commit or rollback message for an unknown participant
+ * must be dropped. If it has then a commit or rollback for an unknown participant must be
+ * acknowledged with, respectively, a committed or aborted message.
+ */
+ public synchronized boolean isParticipantRecoveryStarted()
+ {
+ return participantRecoveryStarted;
+ }
+
+ /**
+ * test whether the first BA coordinator recovery scan has completed. this indicates whether
+ * there may or may not still be unknown BA transcation records on disk. If the first
+ * scan has not yet completed then a prepare message for an unknown participant
+ * must be dropped. If it has then a perpare for an unknown participant must be
+ * acknowledged with a rollback message.
+ */
+ public synchronized boolean isCoordinatorRecoveryStarted() {
+ return coordinatorRecoveryStarted;
+ }
+
+ /**
+ * record the fact that the first BA coordinator recovery scan has completed.
+ */
+
+ public synchronized void setCoordinatorRecoveryStarted() {
+ coordinatorRecoveryStarted = true;
+ }
+
+ /**
+ * a global flag indicating whether the first BA participant recovery scan has
+ * been performed.
+ */
+ private boolean participantRecoveryStarted = false;
+
+ /**
+ * a global flag indicating whether the first AT coordinator recovery scan has
+ * been performed.
+ */
+ private boolean coordinatorRecoveryStarted = false;
+
+ /**
+ * a map from participant ids to participant recovery records
+ */
+ private HashMap<String, BAParticipantRecoveryRecord> recoveryMap = new HashMap<String, BAParticipantRecoveryRecord>();
+
+ /**
+ * a map from participant id to the uid under which the participant has been saved in the
+ * persistent store
+ */
+ private HashMap<String, Uid> uidMap = new HashMap<String, Uid>();
+
+ /**
+ * a map from participant ids to participant recover records
+ */
+ private List<XTSBARecoveryModule> recoveryModules = new ArrayList<XTSBARecoveryModule>();
+
+ /**
+ * the tx object store to be used for saving and deleting participant details
+ */
+ private ObjectStore objectStore;
+
+ private final static String type = BAParticipantRecoveryRecord.type();
+}
\ No newline at end of file
More information about the jboss-svn-commits
mailing list