[jboss-svn-commits] JBL Code SVN: r35272 - in labs/jbosstm/trunk/XTS/demo: src/com/jboss/jbosstm/xts/demo/client and 6 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Fri Sep 24 10:43:13 EDT 2010


Author: adinn
Date: 2010-09-24 10:43:12 -0400 (Fri, 24 Sep 2010)
New Revision: 35272

Added:
   labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/state/ServiceStateConstants.java
Modified:
   labs/jbosstm/trunk/XTS/demo/dd/scripts/demokill.txt
   labs/jbosstm/trunk/XTS/demo/dd/scripts/demorecover.txt
   labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/client/BasicClient.java
   labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoATRecoveryModule.java
   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/restaurant/RestaurantManager.java
   labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantParticipantAT.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/RestaurantServiceAT.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/restaurant/RestaurantView.java
   labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/state/ServiceStateManager.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/TaxiParticipantAT.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/TheatreParticipantAT.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/TheatreServiceAT.java
   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/TheatreView.java
   labs/jbosstm/trunk/XTS/demo/web/index.jsp
Log:
factored out demo local service state management from transactional state management and changed theatre and taxi to use coordinator completion -- fixes for JBTM-790

Modified: labs/jbosstm/trunk/XTS/demo/dd/scripts/demokill.txt
===================================================================
--- labs/jbosstm/trunk/XTS/demo/dd/scripts/demokill.txt	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/dd/scripts/demokill.txt	2010-09-24 14:43:12 UTC (rev 35272)
@@ -64,47 +64,56 @@
 DO traceln("log", "started logging kill  -- point = " + System.getProperty("demo.kill.point"))
 ENDRULE
 
+# identify the theatre manager
+RULE identify theatre manager
+CLASS TheatreManager
+METHOD <init>
+AT EXIT
+IF TRUE
+DO flag($0)
+ENDRULE
+
 # kill the demo just before the theatre service prepares local changes
 RULE kill at kill point 1
-CLASS TheatreManager
-METHOD prepareSeats
+CLASS ServiceStateManager
+METHOD prepare
 AT CALL writeShadowState
 BIND killPoint =  System.getProperty("demo.kill.point")
-IF killPoint == "1"
+IF flagged($0) && killPoint == "1"
 DO traceln("killing JVM at point " + killPoint),
    killJVM()
 ENDRULE
 
 # kill the demo just before XTS logs the recovery record
 RULE kill at kill point 2
-CLASS TheatreManager
-METHOD prepareSeats
+CLASS ServiceStateManager
+METHOD prepare
 AT RETURN
 BIND killPoint =  System.getProperty("demo.kill.point")
-IF killPoint == "2"
+IF flagged($0) && killPoint == "2"
 DO traceln("killing JVM at point " + killPoint),
    killJVM()
 ENDRULE
 
 # kill the demo just before the theatre service commits local changes
 RULE kill at kill point 3
-CLASS TheatreManager
-METHOD commitSeats
+CLASS ServiceStateManager
+METHOD commit
 AT ENTRY
 BIND killPoint =  System.getProperty("demo.kill.point"),
      nullValue : Object = null
-IF killPoint == "3" || killPoint == nullValue
+IF flagged($0) && (killPoint == "3" || killPoint == nullValue)
 DO traceln("killing JVM at point " + killPoint),
    killJVM()
 ENDRULE
 
 # kill the demo just before XTS notifies COMPLETED
 RULE kill at kill point 4
-CLASS TheatreManager
-METHOD commitSeats
+CLASS ServiceStateManager
+METHOD commit
 AT RETURN
 BIND killPoint =  System.getProperty("demo.kill.point")
-IF killPoint == "4"
+IF flagged($0) && killPoint == "4"
 DO traceln("killing JVM at point " + killPoint),
    killJVM()
 ENDRULE

Modified: labs/jbosstm/trunk/XTS/demo/dd/scripts/demorecover.txt
===================================================================
--- labs/jbosstm/trunk/XTS/demo/dd/scripts/demorecover.txt	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/dd/scripts/demorecover.txt	2010-09-24 14:43:12 UTC (rev 35272)
@@ -42,65 +42,38 @@
    traceln("log", "shadow =  " + $shadow)
 ENDRULE
 
-# track recovery of restaurant participant
-# triggered in 1-4
-RULE trace restaurant recovery
-CLASS RestaurantManager
+# track recovered call to restaurant and theatre managers during recovery
+# for restaurant participant AT triggered in 1,2 with $! == true
+# for restaurant participant BA triggered in 1-4 with $! == false
+# for theatre participant AT triggered in 3 with $! = true
+# and in 4 with $! = false
+# for theatre participant BA triggered in 3 with $! = true
+# and in 4 with $! = false
+RULE trace service state
+CLASS ServiceStateManager
 METHOD recovered
 AT EXIT
 IF TRUE
-DO traceln("log", "recovered restaurant participant " + $1)
+DO traceln("log", "recovered tx " + $1 + " type " + $2 + " ==> " + $!)
 ENDRULE
 
-# track call to confirm completed for recovered restaurant participant
-# which has prepared but not yet committed its local changes
-# never triggered in 1-4
-RULE trace confirm completed for recovered restaurant participant
-CLASS RestaurantManager
-METHOD recovered
-AT CALL confirmCompleted
-IF TRUE
-DO traceln("log", "confirming complete for " + $1 + " " + $*[0])
-ENDRULE
-
-# track recovery of theatre participant
-# triggered in 2-4
-RULE trace theatre recovery
-CLASS TheatreManager
-METHOD recovered
-AT EXIT
-IF TRUE
-DO traceln("log", "recovered theatre participant " + $1)
-ENDRULE
-
-# track call to confirm completed for recovered theatre participant
-# which has prepared but not yet committed its local changes
-# triggered in 3
-RULE trace confirm completed for recovered theatre participant
-CLASS TheatreManager
-METHOD recovered
-AT CALL confirmCompleted
-IF TRUE
-DO traceln("log", "confirming complete for " + $1 + " " + $*[0])
-ENDRULE
-
 # track call to commitSeats in theatre
 # triggered in 3 for BA
 RULE trace commit seats under local recovery in theatre participant
-CLASS TheatreManager
-METHOD commitSeats
+CLASS ServiceStateManager
+METHOD commit
 IF callerEquals("confirmCompleted")
 AT CALL commitShadowState
-DO traceln("log", "committing shadow theatre state in " + $1)
+DO traceln("log", "committing shadow state for " + $0 + " in " + $1)
 ENDRULE
 
 # track call to rollbackSeats in theatre
 # triggered in 2 for AT and BA
 RULE trace rollback seats under local recovery in theatre participant
-CLASS TheatreManager
-METHOD rollbackSeats
+CLASS ServiceStateManager
+METHOD rollback
 AT CALL clearShadowState
 IF callerEquals("recoveryScanCompleted") OR
    callerEquals("confirmCompleted")
-DO traceln("log", "rolling back shadow theatre state in " + $1)
+DO traceln("log", "rolling back shadow state for " + $0 + " in " + $1)
 ENDRULE

Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/client/BasicClient.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/client/BasicClient.java	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/client/BasicClient.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -393,8 +393,9 @@
 
         // get business logic params from the form submission.
         int restaurantSeats = Integer.parseInt(request.getParameter("restaurant"));
-        int theatreSeats = Integer.parseInt(request.getParameter("theatrecount"));
-        int theatreArea = Integer.parseInt(request.getParameter("theatrearea"));
+        int theatreCircleSeats = Integer.parseInt(request.getParameter("theatrecirclecount"));
+        int theatreStallsSeats = Integer.parseInt(request.getParameter("theatrestallscount"));
+        int theatreBalconySeats = Integer.parseInt(request.getParameter("theatrebalconycount"));
         int taxiCount = Integer.parseInt(request.getParameter("taxi"));
         boolean bookTaxi = (taxiCount >= 1 ? true : false);
 
@@ -405,11 +406,11 @@
         {
             if ("AtomicTransaction".equals(txType))
             {
-                testAtomicTransaction(restaurantSeats, theatreSeats, theatreArea, bookTaxi);
+                testAtomicTransaction(restaurantSeats, theatreCircleSeats, theatreStallsSeats, theatreBalconySeats, bookTaxi);
             }
             else if ("BusinessActivity".equals(txType))
             {
-                if (!testBusinessActivity(restaurantSeats, theatreSeats, theatreArea, bookTaxi))
+                if (!testBusinessActivity(restaurantSeats, theatreCircleSeats, theatreStallsSeats, theatreBalconySeats, bookTaxi))
                 {
                     result = "Transaction cancelled/compensated.";
                 }
@@ -445,7 +446,7 @@
      *
      * @throws Exception for any unexpected errors, such as a failure to commit.
      */
-    private void testAtomicTransaction(int restaurantSeats, int theatreSeats, int theatreArea, boolean bookTaxi) throws Exception
+    private void testAtomicTransaction(int restaurantSeats, int theatreCircleSeats, int theatreStallsSeats, int theatreBalconySeats, boolean bookTaxi) throws Exception
     {
         System.out.println("CLIENT: obtaining userTransaction...");
 
@@ -460,7 +461,15 @@
         System.out.println("CLIENT: calling business Web Services...");
 
         restaurantAT.bookSeats(restaurantSeats);
-        theatreAT.bookSeats(theatreSeats, theatreArea);
+        if (theatreCircleSeats != 0) {
+            theatreAT.bookSeats(theatreCircleSeats, 0);
+        }
+        if (theatreStallsSeats != 0) {
+            theatreAT.bookSeats(theatreStallsSeats, 1);
+        }
+        if (theatreBalconySeats != 0) {
+            theatreAT.bookSeats(theatreBalconySeats, 2);
+        }
         if (bookTaxi)
         {
             taxiAT.bookTaxi();
@@ -484,7 +493,7 @@
      *
      * @throws Exception for any unexpected errors, such as a failure to commit.
      */
-    private boolean testBusinessActivity(int restaurantSeats, int theatreSeats, int theatreArea, boolean bookTaxi) throws Exception
+    private boolean testBusinessActivity(int restaurantSeats, int theatreCircleSeats, int theatreStallsSeats, int theatreBalconySeats, boolean bookTaxi) throws Exception
     {
         System.out.println("CLIENT: obtaining userBusinessActivity...");
 
@@ -498,34 +507,57 @@
 
         System.out.println("CLIENT: calling business Web Services...");
 
-        boolean isOK = false ;
+        boolean isOK = true ;
         try
         {
-            if (restaurantBA.bookSeats(restaurantSeats) && theatreBA.bookSeats(theatreSeats, theatreArea))
+            isOK = restaurantBA.bookSeats(restaurantSeats);
+
+            if (isOK && theatreCircleSeats != 0)
             {
-                isOK = !bookTaxi || taxiBA.bookTaxi() ;
+                isOK = theatreBA.bookSeats(theatreCircleSeats, 0);
             }
+            if (isOK && theatreStallsSeats != 0)
+            {
+                isOK = theatreBA.bookSeats(theatreStallsSeats, 1);
+            }
+            if (isOK && theatreBalconySeats != 0)
+            {
+                isOK = theatreBA.bookSeats(theatreBalconySeats, 2);
+            }
+            if (isOK && bookTaxi)
+            {
+                isOK = taxiBA.bookTaxi();
+            }
         }
         catch (final Throwable th)
         {
-            System.out.println("CLIENT: caught exception processing bookings, cancelling (" + th.getMessage() + ")") ;
+            System.out.println("CLIENT: caught exception processing bookings, cancelling (" + th + ")") ;
+            try {
+                uba.cancel();
+            } catch (Throwable th2) {
+                System.out.println("CLIENT: caught exception cancelling transaction (" + th2 + ")") ;
+            }
+            return false;
         }
 
-        if (isOK)
-        {
-            System.out.println("CLIENT: all OK");
-            System.out.println("CLIENT: calling close on the transaction...");
-            uba.close();
+        if (isOK) {
+            try {
+                System.out.println("CLIENT: calling close on the transaction...");
+                uba.close();
+            } catch (Throwable th) {
+                System.out.println("CLIENT: caught exception  closing transaction (" + th + ")") ;
+                return false;
+            }
+        } else {
+            try {
+                System.out.println("CLIENT: calling cancel on the transaction...");
+                uba.cancel();
+            } catch (Throwable th) {
+                System.out.println("CLIENT: caught exception cancelling transaction (" + th + ")") ;
+                return false;
+            }
         }
-        else
-        {
-            System.out.println("CLIENT: one or more services failed, calling cancel.");
-            uba.cancel();
-        }
-
-        System.out.println("CLIENT: done.");
-        System.out.flush();
-
+        
         return isOK;
     }
 

Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoATRecoveryModule.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoATRecoveryModule.java	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoATRecoveryModule.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -2,8 +2,7 @@
 
 import com.jboss.jbosstm.xts.demo.services.restaurant.RestaurantManager;
 import com.jboss.jbosstm.xts.demo.services.restaurant.RestaurantParticipantAT;
-import com.jboss.jbosstm.xts.demo.services.state.ServiceStateManager;
-import com.jboss.jbosstm.xts.demo.services.taxi.TaxiManager;
+import static com.jboss.jbosstm.xts.demo.services.state.ServiceStateConstants.*;
 import com.jboss.jbosstm.xts.demo.services.taxi.TaxiParticipantAT;
 import com.jboss.jbosstm.xts.demo.services.theatre.TheatreManager;
 import com.jboss.jbosstm.xts.demo.services.theatre.TheatreParticipantAT;
@@ -77,19 +76,18 @@
             System.out.println("xts-demo : attempting to deserialize RestaurantParticipantAT " + id);
             RestaurantParticipantAT participant = (RestaurantParticipantAT)stream.readObject();
             System.out.println("xts-demo : deserialized RestaurantParticipantAT " + id);
-            RestaurantManager.getSingletonInstance().recovered(participant);
+            RestaurantManager.getSingletonInstance().recovered(participant.getTxID(), TX_TYPE_AT);
             return participant;
         } else if (id.startsWith("org.jboss.jbossts.xts-demo:theatreAT")) {
             System.out.println("xts-demo : attempting to deserialize TheatreParticipantAT " + id);
             TheatreParticipantAT participant = (TheatreParticipantAT)stream.readObject();
             System.out.println("xts-demo : deserialized TheatreParticipantAT " + id);
-            TheatreManager.getSingletonInstance().recovered(participant);
+            TheatreManager.getSingletonInstance().recovered(participant.getTxID(), TX_TYPE_AT);
             return participant;
         } else if (id.startsWith("org.jboss.jbossts.xts-demo:taxiAT")) {
             System.out.println("xts-demo : attempting to deserialize TaxiParticipantAT " + id);
             TaxiParticipantAT participant = (TaxiParticipantAT)stream.readObject();
             System.out.println("xts-demo : deserialized TaxiParticipantAT " + id);
-            TaxiManager.getSingletonInstance().recovered(participant);
             return participant;
         }
 
@@ -134,11 +132,9 @@
     public void endScan()
     {
         if (isFirst) {
-            // both AT and BA participants update state. so let the state manager know that the
-            // AT log records have all been scanned
-            RestaurantManager.getSingletonInstance().recoveryScanCompleted(ServiceStateManager.TX_TYPE_AT);
-            TheatreManager.getSingletonInstance().recoveryScanCompleted(ServiceStateManager.TX_TYPE_AT);
-            TaxiManager.getSingletonInstance().recoveryScanCompleted(ServiceStateManager.TX_TYPE_AT);
+            // let the restaurant and theatre state manager know that the AT log records have all been scanned
+            RestaurantManager.getSingletonInstance().recoveryScanCompleted(TX_TYPE_AT);
+            TheatreManager.getSingletonInstance().recoveryScanCompleted(TX_TYPE_AT);
             isFirst = false;
         }
     }

Modified: 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	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoBARecoveryModule.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -2,8 +2,7 @@
 
 import com.jboss.jbosstm.xts.demo.services.restaurant.RestaurantManager;
 import com.jboss.jbosstm.xts.demo.services.restaurant.RestaurantParticipantBA;
-import com.jboss.jbosstm.xts.demo.services.state.ServiceStateManager;
-import com.jboss.jbosstm.xts.demo.services.taxi.TaxiManager;
+import static com.jboss.jbosstm.xts.demo.services.state.ServiceStateConstants.*;
 import com.jboss.jbosstm.xts.demo.services.taxi.TaxiParticipantBA;
 import com.jboss.jbosstm.xts.demo.services.theatre.TheatreManager;
 import com.jboss.jbosstm.xts.demo.services.theatre.TheatreParticipantBA;
@@ -79,21 +78,14 @@
             RestaurantParticipantBA participant = (RestaurantParticipantBA)stream.readObject();
             // ensure that the participant has completed any changes to local service state.
             System.out.println("xts-demo : deserialized RestaurantParticipantBA " + id);
-            RestaurantManager.getSingletonInstance().recovered(participant);
+            if (RestaurantManager.getSingletonInstance().recovered(participant.getTxID(), TX_TYPE_BA)) {
+                participant.confirmCompleted(true);
+            }
             return participant;
-        } else if (id.startsWith("org.jboss.jbossts.xts-demo:theatreBA")) {
-            System.out.println("xts-demo : attempting to deserialize TheatreParticipantBA " + id);
-            TheatreParticipantBA participant = (TheatreParticipantBA)stream.readObject();
-            // ensure that the participant has completed any changes to local service state.
-            System.out.println("xts-demo : deserialized TheatreParticipantBA " + id);
-            TheatreManager.getSingletonInstance().recovered(participant);
-            return participant;
-        } else if (id.startsWith("org.jboss.jbossts.xts-demo:taxiBA")) {
-            System.out.println("xts-demo : attempting to deserialize TaxiParticipantBA " + id);
-            TaxiParticipantBA participant = (TaxiParticipantBA)stream.readObject();
-            System.out.println("xts-demo : deserialized TaxiParticipantBA " + id);
-            TaxiManager.getSingletonInstance().recovered(participant);
-            return participant;
+        } else if (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 theatre and taxi participants employ the CoordinatorCompletion protocol
+            throw new Exception("xts-demo : invalid request to deserialize as WS-BA ParticipantCompletion participant " + id);
         }
         return null;
     }
@@ -137,11 +129,24 @@
      * @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);
+        if (id.startsWith("org.jboss.jbossts.xts-demo:restauarantBA")) {
+            // this should not get called -- xts-demo WS-BA restaurant participants employ the ParticipantCompletion protocol
+            throw new Exception("xts-demo : invalid request to deserialize as WS-BA CoordinatorCompletion participant " + id);
+        } else if (id.startsWith("org.jboss.jbossts.xts-demo:theatreBA")) {
+            System.out.println("xts-demo : attempting to deserialize TheatreParticipantBA " + id);
+            TheatreParticipantBA participant = (TheatreParticipantBA)stream.readObject();
+            // ensure that the participant has completed any changes to local service state.
+            System.out.println("xts-demo : deserialized TheatreParticipantBA " + id);
+            if (TheatreManager.getSingletonInstance().recovered(participant.getTxID(), TX_TYPE_BA)) {
+                participant.confirmCompleted(true);
+            }
+            return participant;
+        } else if (id.startsWith("org.jboss.jbossts.xts-demo:taxiBA")) {
+            System.out.println("xts-demo : attempting to deserialize TaxiParticipantBA " + id);
+            TaxiParticipantBA participant = (TaxiParticipantBA)stream.readObject();
+            System.out.println("xts-demo : deserialized TaxiParticipantBA " + id);
+            participant.confirmCompleted(true);
+            return participant;
         }
         return null;
     }
@@ -186,11 +191,9 @@
     public void endScan()
     {
         if (isFirst) {
-            // both AT and BA participants update state. so let the state manager know that the
-            // BA log records have all been scanned
-            RestaurantManager.getSingletonInstance().recoveryScanCompleted(ServiceStateManager.TX_TYPE_BA);
-            TheatreManager.getSingletonInstance().recoveryScanCompleted(ServiceStateManager.TX_TYPE_BA);
-            TaxiManager.getSingletonInstance().recoveryScanCompleted(ServiceStateManager.TX_TYPE_BA);
+            // let the restaurant and theatre state manager know that the BA log records have all been scanned
+            RestaurantManager.getSingletonInstance().recoveryScanCompleted(TX_TYPE_BA);
+            TheatreManager.getSingletonInstance().recoveryScanCompleted(TX_TYPE_BA);
             isFirst = false;
         }
     }

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	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantManager.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -35,7 +35,7 @@
 import java.io.*;
 
 /**
- * The transactional application logic for the Restaurant Service.
+ * The application logic for the Restaurant Service.
  * <p/>
  * Stores and manages seating reservations.
  * <p/>
@@ -48,12 +48,9 @@
  * roll back. Conflict detection is implemented using a simple versioning scheme.
  *
  * The restaurant manager provides a book method allowing the web service endpoint to book
- * or unbook seats. It also exposes prepare, commit and rollback operations used by both
- * WSAT and WSBA participants to drive prepare, commit and rollback of changes to the
- * persistent state. Finally it exposes recovery logic used by the WSAT and WSBA recovery
- * modules to tie recovery of WSAT and WSBA participants to recovery and rollback of the
- * local service state.
- *
+ * or unbook seats. It also provides getters which allow the GUI to  monitor the state
+ * of the service while transactions are in progress.
+ * 
  * @author Jonathan Halliday (jonathan.halliday at arjuna.com)
  * @author Andrew Dinn (adinn at redhat.com)
  * @version $Revision:$
@@ -61,7 +58,7 @@
 public class RestaurantManager extends ServiceStateManager<RestaurantState> {
 
     /*****************************************************************************/
-    /* Support for the Web Service API                                           */
+    /* Support for the Web Services                                              */
     /*****************************************************************************/
 
     /**
@@ -97,14 +94,14 @@
             }
         }
 
-        if (restaurantState.freeSeats < nSeats ||
-                restaurantState.bookedSeats + nSeats > restaurantState.totalSeats) {
+        if (currentState.freeSeats < nSeats ||
+                currentState.bookedSeats + nSeats > currentState.totalSeats) {
             throw new WebServiceException("requested number of seats (" + nSeats + ") not available");
         }
 
         // create a state derived from the current state which reflects the new booking count
 
-        RestaurantState childState = restaurantState.derivedState();
+        RestaurantState childState = currentState.derivedState();
 
         // update the number of booked and free seats in the derived state
 
@@ -117,140 +114,52 @@
 
     }
 
-    /**
-     * check whether we have already seen a web service request in a given transaction
-     */
-
-    public synchronized boolean knowsAbout(Object txID)
-    {
-        return getState(txID) != null;
-    }
-
     /*****************************************************************************/
-    /* Support for the AT and BA Participant API implementation                  */
+    /* Implementation of inherited abstract state management API                 */
     /*****************************************************************************/
 
     /**
-     * Prepare local state changes for the supplied transaction
-     *
-     * @param txID The transaction identifier
-     * @return true on success, false otherwise
-     */
-    public boolean prepareSeats(Object txID)
+     * method called during prepare of local state changes allowing the user to force a prepare failure
+     * @return true if the prepare should succeed and false if it should fail
+    */
+    public boolean confirmPrepare()
     {
-        // ensure that we have seen this transaction before
-        RestaurantState childState = getState(txID);
-        if (childState == null) {
-            return false;
-        }
-        // we have a single monolithic state element which means that only one transaction can prepare
-        // at any given time. we lock this state at prepare by providing the txId as a locking id. it only
-        // gets unlocked when we reach commit or rollback. the equivalent to the lock in memory is the
-        // shadow state file on disk.
-        synchronized (this) {
-            while (isLocked()) {
+        if (autoCommitMode) {
+            return true;
+        } else {
+            // need to wait for the user to decide whether to go ahead or not with this participant
+            isPreparationWaiting = true;
+            synchronized (preparation) {
                 try {
-                    wait();
+                    preparation.wait();
                 } catch (InterruptedException e) {
                     // ignore
                 }
             }
+            isPreparationWaiting = false;
 
-            // check no other bookings have been committed
-
-            if (!restaurantState.isParentOf(childState)) {
-                removeState(txID);
-                return false;
-            }
-
-            // see if we need user confirmation
-
-            if (!autoCommitMode) {
-                // need to wait for the user to decide whether to go ahead or not with this participant
-                isPreparationWaiting = true;
-                synchronized (preparation) {
-                    try {
-                        preparation.wait();
-                    } catch (InterruptedException e) {
-                        // ignore
-                    }
-                }
-                isPreparationWaiting = false;
-
-                // process the user decision
-                if (!isCommit) {
-                    removeState(txID);
-                    return false;
-                }
-            }
-
-            // ok, so lock the state against other prepare/commits
-
-            lock(txID);
+            return isCommit;
         }
-        // if we got here then no other changes have invalidated our booking and we have locked out
-        // further changes until commit or rollback occurs. we write the derived child state to the
-        // shadow state file before returning. if we crash after the write we will detect the shadow
-        // state at reboot and restore the lock.
-        try {
-            writeShadowState(txID, childState);
-            return true;
-        } catch (Exception e) {
-            clearShadowState(txID);
-            synchronized (this) {
-                removeState(txID);
-                unlock();
-            }
-            System.err.println("RestaurantManager.prepareSeats(): Error attempting to prepare transaction: " + e);
-            return false;
-        }
     }
 
     /**
-     * commit local state changes for the supplied transaction
-     *
-     * @param txID
+     * identify the name of file used to store the current service state
+      * @return the name of the file used to store the current service state
      */
-    public void commitSeats(Object txID)
-    {
-        synchronized (this) {
-            // if there is a shadow state with this id then we need to copy the shadow state file over to the
-            // real state file. it may be that there is no shadow state because this is a repeated commit
-            // request. if so then we must have committed earlier so there is no harm done.
-            if (isLockID(txID)) {
-                commitShadowState(txID);
-                // update the current state with the prepared state.
-                restaurantState = getPreparedState();
-                unlock();
-            }
-            removeState(txID);
-        }
+    @Override
+    public String getStateFilename() {
+        return STATE_FILENAME;
     }
 
     /**
-     * roll back local state changes for the supplied transaction
-     * @param txID
+     * identify the name of file used to store the shadow service state
+      * @return the name of the file used to store the shadow service state
      */
-    public void rollbackSeats(Object txID)
-    {
-        synchronized (this) {
-            removeState(txID);
-            if (isLockID(txID)) {
-                clearShadowState(txID);
-                unlock();
-            }
-        }
+    @Override
+    public String getShadowStateFilename() {
+        return SHADOW_STATE_FILENAME;
     }
 
-    /**
-     * handle a recovery error by rolling back the changes associated with the transaction
-     * @param txID
-     */
-    public void error(String txID)
-    {
-        rollbackSeats(txID);
-    }
-
     /*****************************************************************************/
     /* Accessors for the GUI to view and reset the service state                 */
     /*****************************************************************************/
@@ -272,7 +181,7 @@
 
         // undo all existing bookings
 
-        RestaurantState resetState = restaurantState.derivedState();
+        RestaurantState resetState = currentState.derivedState();
 
         resetState.totalSeats = DEFAULT_SEATING_CAPACITY;
         resetState.bookedSeats = 0;
@@ -287,7 +196,7 @@
              System.out.println("error : unable to reset restaurant manager state " + e);
         }
 
-        restaurantState = resetState;
+        currentState = resetState;
         
         // remove any in-progress transactions
 
@@ -301,7 +210,7 @@
      */
     public int getNFreeSeats()
     {
-        return restaurantState.freeSeats;
+        return currentState.freeSeats;
     }
 
     /**
@@ -311,7 +220,7 @@
      */
     public int getNTotalSeats()
     {
-        return restaurantState.totalSeats;
+        return currentState.totalSeats;
     }
 
     /**
@@ -321,7 +230,7 @@
      */
     public int getNBookedSeats()
     {
-        return restaurantState.bookedSeats;
+        return currentState.bookedSeats;
     }
 
     /**
@@ -333,7 +242,7 @@
     {
         if (isLocked()) {
             RestaurantState childState = getPreparedState();
-            return childState.bookedSeats - restaurantState.bookedSeats;
+            return childState.bookedSeats - currentState.bookedSeats;
         } else {
             return 0;
         }
@@ -400,65 +309,6 @@
     }
 
     /*****************************************************************************/
-    /* Implementation of inherited abstract sate management API                  */
-    /*****************************************************************************/
-
-    /**
-     * identify the name of file used to store the current service state
-      * @return the name of the file used to store the current service state
-     */
-    @Override
-    public String getStateFilename() {
-        return STATE_FILENAME;
-    }
-
-    /**
-     * identify the name of file used to store the shadow service state
-      * @return the name of the file used to store the shadow service state
-     */
-    @Override
-    public String getShadowStateFilename() {
-        return SHADOW_STATE_FILENAME;
-    }
-
-    /*****************************************************************************/
-    /* Recovery methods maintaining consistency of local and  WSAT/WSBA state    */
-    /*****************************************************************************/
-
-    /**
-     * called by the AT recovery module when an AT participant is recovered from a log record
-     */
-    public void recovered(RestaurantParticipantAT participant)
-    {
-        // if this AT participant matches the prepared TX id then we need to leave it prepared and locked
-        // at the end of scanning so it can be completed at commit time
-        if (isLockID(participant.txID)) {
-            rollbackPreparedTx = false;
-        }
-    }
-
-    /**
-     * called by the BA recovery module when an AT participant is recovered from a log record
-     */
-    public void recovered(RestaurantParticipantBA participant)
-    {
-        // if this AT participant matches the prepared TX id then we roll it forward here by calling
-        // confirmCompleted so once again we don't need to roll back the prepared state
-        if (isLockID(participant.txID)) {
-            participant.confirmCompleted(true);
-            rollbackPreparedTx = false;
-        }
-    }
-
-    public void recoveryScanCompleted(int txType)
-    {
-        super.recoveryScanCompleted(txType);
-        if (completedScans == TX_TYPE_BOTH && rollbackPreparedTx) {
-            rollbackSeats(getLockID());
-        }
-    }
-
-    /*****************************************************************************/
     /* Private implementation                                                    */
     /*****************************************************************************/
 
@@ -490,22 +340,6 @@
     private boolean isCommit;
 
     /**
-     * Flag which determines whether we have to roll back any prepared changes to the server. We roll back
-     * changes for an AT or BA participant if there is no associated log record because the participant never
-     * prepared or completed, respectively. If we see a log record for an AT participant we leave the prepared
-     * state behind since the AT participant has prepared and may still commit.
-     */
-    private boolean rollbackPreparedTx;
-
-    /**
-     * the latest version of the restaurant state which includes a version id.
-     * this state object is always stored on disk in the restaurant state file
-     * a prepared version of a single derived child state may also exist on disk
-     * in the restaurant shadow state file.
-     */
-    private RestaurantState restaurantState;
-
-    /**
      * Create and initialise a new RestaurantManager instance either restoring any
      * existing service state from disk or else installing and committing to disk
      * a new initial state. If a prepared version of a derived child state (shadow state)
@@ -515,13 +349,13 @@
      */
     private RestaurantManager()
     {
-        RestaurantState restoredState = restoreState();
-        if (restoredState == null) {
+        super();
+        if (currentState == null) {
             // we need to create a new initial state and persist it to disk
-            restoredState = RestaurantState.initialState();
+            currentState = RestaurantState.initialState();
             Object txId = "initialisation-transaction-" + System.currentTimeMillis();
             try {
-                writeShadowState(txId, restoredState);
+                writeShadowState(txId, currentState);
                 commitShadowState(txId);
             } catch (IOException e) {
                 clearShadowState(txId);
@@ -529,12 +363,7 @@
             }
         }
 
-        restaurantState = restoredState;
-        
         preparation = new Object();
-        // we will roll back any locally prepared changes to web service state unless we discover that
-        // they are needed during recovery
-        rollbackPreparedTx = isLocked();
 
         isCommit = true;
         autoCommitMode = true;

Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantParticipantAT.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantParticipantAT.java	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantParticipantAT.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -32,6 +32,7 @@
 import com.arjuna.wst.*;
 
 import java.io.Serializable;
+import java.util.HashMap;
 
 /**
  * An adapter class that exposes the RestaurantManager transaction lifecycle
@@ -57,8 +58,18 @@
         // we need to save the txID for later use when calling
         // business logic methods in the restaurantManger.
         this.txID = txID;
+        // we may invalidate the participant later if something goes wrong
+        this.valid = true;
     }
 
+    /**
+     * accessor for participant transaction id
+     * @return the participant transaction id
+     */
+    public String getTxID() {
+        return txID;
+    }
+
     /************************************************************************/
     /* Durable2PCParticipant methods                                        */
     /************************************************************************/
@@ -79,7 +90,7 @@
 
         getRestaurantView().addPrepareMessage("id:" + txID + ". Prepare called on participant: " + this.getClass().toString());
 
-        boolean success = getRestaurantManager().prepareSeats(txID);
+        boolean success = getRestaurantManager().prepare(txID);
 
         // Log the outcome and map the return value from
         // the business logic to the appropriate Vote type.
@@ -94,6 +105,8 @@
         {
             getRestaurantView().addMessage("Prepare failed (not enough seats?) Returning 'Aborted'\n");
             getRestaurantView().updateFields();
+            // forget about the participant
+            removeParticipant(txID);
             return new Aborted();
         }
     }
@@ -114,13 +127,16 @@
 
         getRestaurantView().addMessage("id:" + txID + ". Commit called on participant: " + this.getClass().toString());
 
-        getRestaurantManager().commitSeats(txID);
+        getRestaurantManager().commit(txID);
 
         // Log the outcome
 
         getRestaurantView().addMessage("Seats committed\n");
 
         getRestaurantView().updateFields();
+
+        // forget about the participant
+        removeParticipant(txID);
     }
 
     /**
@@ -139,24 +155,79 @@
 
         getRestaurantView().addMessage("id:" + txID + ". Rollback called on participant: " + this.getClass().toString());
 
-        getRestaurantManager().rollbackSeats(txID);
+        getRestaurantManager().rollback(txID);
 
         getRestaurantView().addMessage("Seats booking cancelled\n");
         
         getRestaurantView().updateFields();
+        // forget about the participant
+        removeParticipant(txID);
     }
 
     public void unknown() throws SystemException
     {
-        // used for calbacks during crash recovery. This impl is not recoverable
+        // forget about the participant
+        participants.put(txID, null);
     }
 
     public void error() throws SystemException
     {
-        // used for calbacks during crash recovery. This impl is not recoverable
+        // forget about the participant
+        participants.put(txID, null);
     }
 
     /************************************************************************/
+    /* tracking active participants                                         */
+    /************************************************************************/
+    /**
+     * keep track of a participant
+     * @param txID
+     * @param participant
+     */
+    public static synchronized void recordParticipant(String txID, RestaurantParticipantAT participant)
+    {
+        participants.put(txID, participant);
+    }
+
+    /**
+     * forget about a participant
+     * @param txID
+     * @param participant
+     */
+    public static synchronized RestaurantParticipantAT removeParticipant(String txID)
+    {
+        return participants.remove(txID);
+    }
+
+    /**
+     * lookup a participant
+     * @param txID
+     * @return the participant
+     */
+    public static synchronized RestaurantParticipantAT getParticipant(String txID)
+    {
+        return participants.get(txID);
+    }
+
+    /**
+     * mark a participant as invalid
+     */
+    public void invalidate()
+    {
+        valid = false;
+    }
+
+    /**
+     * check if a participant is invalid
+     *
+     * @return
+     */
+    public boolean isValid()
+    {
+        return valid;
+    }
+
+    /************************************************************************/
     /* private implementation                                               */
     /************************************************************************/
     /**
@@ -165,12 +236,22 @@
      * is passed to the backend business logic methods.
      */
     protected String txID;
+    /**
+     * this is true by default but we invalidate the participant if the client makes invalid requests
+     */
+    protected boolean valid;
 
-    public RestaurantView getRestaurantView() {
+    private RestaurantView getRestaurantView() {
         return RestaurantView.getSingletonInstance();
     }
 
-    public RestaurantManager getRestaurantManager() {
+    private RestaurantManager getRestaurantManager() {
         return RestaurantManager.getSingletonInstance();
     }
+
+    /**
+     * table of currently active participants
+     */
+    private static HashMap<String, RestaurantParticipantAT> participants = new HashMap<String,  RestaurantParticipantAT>();
+
 }

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	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantParticipantBA.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -33,12 +33,17 @@
 import com.arjuna.wst11.ConfirmCompletedParticipant;
 
 import java.io.Serializable;
+import java.util.HashMap;
 
 /**
  * An adapter class that exposes the RestaurantManager transaction lifecycle
- * API as a WS-T Business Activity participant.
+ * API as a WS-T Participant Completion Business Activity participant.
  * Also logs events to a RestaurantView object.
  *
+ * The Restaurant Service only allows a single booking in any given transaction. So, this
+ * means it can complete at the end of the booking call. Hence it uses a participant which
+ * implements the participant completion protocol.
+ *
  * @author Jonathan Halliday (jonathan.halliday at arjuna.com)
  * @version $Revision: 1.3 $
  */
@@ -59,11 +64,20 @@
      */
     public RestaurantParticipantBA(String txID, int how_many)
     {
-        // we need to save the txID for later use when logging.
+        // we need to save the txID for later use when logging
+        // and the seat count for use during compensation
         this.txID = txID;
         this.seatCount = how_many;
     }
 
+    /**
+     * accessor for participant transaction id
+     * @return the participant transaction id
+     */
+    public String getTxID() {
+        return txID;
+    }
+
     /************************************************************************/
     /* BusinessAgreementWithParticipantCompletionParticipant methods        */
     /************************************************************************/
@@ -84,6 +98,8 @@
         getRestaurantView().addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
 
         getRestaurantView().updateFields();
+
+        removeParticipant(txID);
     }
 
 
@@ -102,11 +118,13 @@
 
         System.out.println("RestaurantParticipantBA.cancel");
 
-        getRestaurantManager().rollbackSeats(txID);
+        getRestaurantManager().rollback(txID);
         
         getRestaurantView().addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
 
         getRestaurantView().updateFields();
+
+        removeParticipant(txID);
     }
 
     /**
@@ -126,23 +144,27 @@
 
         getRestaurantView().updateFields();
 
-        // we perform the compensation by preparing and then committing a change which
+        // we perform the compensation by preparing and then committing a local change which
         // decrements the bookings
 
         String compensationTxID = txID + "-compensation";
 
         getRestaurantManager().bookSeats(compensationTxID, -seatCount);
         
-        if (!getRestaurantManager().prepareSeats(compensationTxID)) {
+        if (!getRestaurantManager().prepare(compensationTxID)) {
             getRestaurantView().addMessage("id:" + txID + ". Failed to compensate participant: " + this.getClass().toString());
 
+            removeParticipant(txID);
+
             throw new FaultedException("Failed to compensate participant: " + this.getClass().toString());
         }
-        getRestaurantManager().commitSeats(compensationTxID);
+        getRestaurantManager().commit(compensationTxID);
         
         getRestaurantView().addMessage("id:" + txID + ". Compensated participant: " + this.getClass().toString());
 
         getRestaurantView().updateFields();
+
+        removeParticipant(txID);
     }
 
     public String status()
@@ -162,13 +184,15 @@
 
         getRestaurantView().updateFields();
 
-        // tell the manager we had an error
+        // ensure local prepared state is rolled back
 
-        getRestaurantManager().error(txID);
+        getRestaurantManager().rollback(txID);
 
         getRestaurantView().addMessage("id:" + txID + ". Notified error for participant: " + this.getClass().toString());
 
         getRestaurantView().updateFields();
+
+        removeParticipant(txID);
     }
 
     /************************************************************************/
@@ -185,13 +209,50 @@
 
     public void confirmCompleted(boolean confirmed) {
         if (confirmed) {
-            getRestaurantManager().commitSeats(txID);
+            getRestaurantManager().commit(txID);
+            getRestaurantView().addMessage("id:" + txID + ". Seats committed");
+            getRestaurantView().updateFields();
         } else {
-            getRestaurantManager().rollbackSeats(txID);
+            getRestaurantManager().rollback(txID);
+            getRestaurantView().addMessage("id:" + txID + ". Seats rolled back");
+            getRestaurantView().updateFields();
         }
     }
 
     /************************************************************************/
+    /* tracking active participants                                         */
+    /************************************************************************/
+    /**
+     * keep track of a participant
+     * @param txID
+     * @param participant
+     */
+    public static synchronized void recordParticipant(String txID, RestaurantParticipantBA participant)
+    {
+        participants.put(txID, participant);
+    }
+
+    /**
+     * forget about a participant
+     * @param txID
+     * @param participant
+     */
+    public static synchronized RestaurantParticipantBA removeParticipant(String txID)
+    {
+        return participants.remove(txID);
+    }
+
+    /**
+     * lookup a participant
+     * @param txID
+     * @return the participant
+     */
+    public static synchronized RestaurantParticipantBA getParticipant(String txID)
+    {
+        return participants.get(txID);
+    }
+
+    /************************************************************************/
     /* private implementation                                               */
     /************************************************************************/
     /**
@@ -213,5 +274,10 @@
     private RestaurantManager getRestaurantManager() {
         return RestaurantManager.getSingletonInstance();
     }
+
+    /**
+     * table of currently active participants
+     */
+    private static HashMap<String, RestaurantParticipantBA> participants = new HashMap<String,  RestaurantParticipantBA>();
 }
 

Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantServiceAT.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantServiceAT.java	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantServiceAT.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -33,18 +33,13 @@
 import com.arjuna.mw.wst11.TransactionManagerFactory;
 import com.arjuna.mw.wst11.UserTransactionFactory;
 import com.jboss.jbosstm.xts.demo.restaurant.IRestaurantServiceAT;
-import com.jboss.jbosstm.xts.demo.services.recovery.DemoATRecoveryModule;
 
 import javax.jws.HandlerChain;
 import javax.jws.WebParam;
 import javax.jws.WebService;
 import javax.jws.WebMethod;
 import javax.jws.soap.SOAPBinding;
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
 
-import org.jboss.jbossts.xts.recovery.participant.at.XTSATRecoveryManager;
-
 /**
  * An adapter class that exposes the RestaurantManager business API as a
  * transactional Web Service. Also logs events to a RestaurantView object.
@@ -81,13 +76,27 @@
             transactionId = UserTransactionFactory.userTransaction().toString();
             System.out.println("RestaurantServiceAT transaction id =" + transactionId);
 
-            if (!restaurantManager.knowsAbout(transactionId))
+            RestaurantParticipantAT restaurantParticipant = RestaurantParticipantAT.getParticipant(transactionId);
+
+            if (restaurantParticipant != null)
             {
-                System.out.println("RestaurantServiceAT - enrolling...");
-                // enlist the Participant for this service:
-                RestaurantParticipantAT restaurantParticipant = new RestaurantParticipantAT(transactionId);
-                TransactionManagerFactory.transactionManager().enlistForDurableTwoPhase(restaurantParticipant, "org.jboss.jbossts.xts-demo:restaurantAT:" + new Uid().toString());
+                // this service does not support repeated bookings in the same transaction
+                // so mark the participant as invalid
+                restaurantView.addMessage("id:" + transactionId + ". Participant already enrolled!");
+                restaurantView.updateFields();
+                System.err.println("bookSeats: request failed");
+                // this ensures we do not try later to prepare the participant
+                restaurantParticipant.invalidate();
+                // throw away any local changes previously made on behalf of the participant
+                restaurantManager.rollback(transactionId);
+                return;
             }
+
+            System.out.println("RestaurantServiceAT - enrolling...");
+            // enlist the Participant for this service:
+            restaurantParticipant = new RestaurantParticipantAT(transactionId);
+            TransactionManagerFactory.transactionManager().enlistForDurableTwoPhase(restaurantParticipant, "org.jboss.jbossts.xts-demo:restaurantAT:" + new Uid().toString());
+            RestaurantParticipantAT.recordParticipant(transactionId, restaurantParticipant);
         }
         catch (Exception e)
         {

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	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantServiceBA.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -43,6 +43,14 @@
  * An adapter class that exposes the RestaurantManager business API as a
  * transactional Web Service. Also logs events to a RestaurantView object.
  *
+ * The BA Restaurant Service only allows the client to make one booking in any given transaction.
+ * So, this means that it can complete its changes as soon as the booking has been made. Hence
+ * it uses a participant which implements the participant completion protocol. When the client
+ * closes the activity the coordinator will ensure that the participant has completed then
+ * it only has to send a CLOSE message. This means that if the client cancels the activity after
+ * booking the restaurant then the coordinator cannot just CANCEL the participant. It will have to
+ * send a COMPENSATE message in order to make sure that the committed booking is undone.
+ *
  * @author Jonathan Halliday (jonathan.halliday at arjuna.com)
  * @version $Revision: 1.5 $
  */
@@ -90,10 +98,12 @@
 
         restaurantView.addMessage("******************************");
 
-        restaurantView.addPrepareMessage("id:" + transactionId + ". Received a booking request for one table of " + how_many + " people");
+        restaurantView.addMessage("id:" + transactionId + ". Received a booking request for one table of " + how_many + " people");
         restaurantView.updateFields();
 
-        if (restaurantManager.knowsAbout(transactionId)) {
+        RestaurantParticipantBA restaurantParticipant = RestaurantParticipantBA.getParticipant(transactionId);
+
+        if (restaurantParticipant != null) {
             // hmm, this means we have already completed changes in this transaction and are awaiting a close
             //or compensate request. this service does not support repeated requests in the same activity so
             // we fail this request.
@@ -104,24 +114,25 @@
             return false;
         }
 
-        RestaurantParticipantBA restaurantParticipant = new RestaurantParticipantBA(transactionId, how_many);
-
         BAParticipantManager participantManager;
 
         // enlist the Participant for this service:
         try
         {
+            restaurantParticipant = new RestaurantParticipantBA(transactionId, how_many);
             participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(restaurantParticipant, "org.jboss.jbossts.xts-demo:restaurantBA:" + new Uid().toString());
+            RestaurantParticipantBA.recordParticipant(transactionId, restaurantParticipant);
         }
         catch (Exception e)
         {
             restaurantView.addMessage("id:" + transactionId + ". Participant enrolement failed");
-            restaurantManager.rollbackSeats(transactionId);
             System.err.println("bookSeats: Participant enlistment failed");
             e.printStackTrace(System.err);
             return false;
         }
 
+        restaurantView.addPrepareMessage("id:" + transactionId + ". Attempting to prepare seats");
+        restaurantView.updateFields();
         // invoke the backend business logic:
         restaurantManager.bookSeats(transactionId, how_many);
 
@@ -129,9 +140,9 @@
         // commit local changes. so we prepare and commit those changes now. if any other participant fails
         // or the client decides to cancel we can rely upon being told to compensate.
 
-        if (restaurantManager.prepareSeats(transactionId))
+        if (restaurantManager.prepare(transactionId))
         {
-            restaurantView.addMessage("id:" + transactionId + ". Seats prepared, trying to commit and enlist compensation Participant");
+            restaurantView.addMessage("id:" + transactionId + ". Seats prepared, trying to commit");
             restaurantView.updateFields();
 
             try
@@ -144,8 +155,9 @@
             catch (Exception e)
             {
                 System.err.println("bookSeats: 'completed' callback failed");
-                restaurantManager.rollbackSeats(transactionId);
+                restaurantManager.rollback(transactionId);
                 e.printStackTrace(System.err);
+                RestaurantParticipantBA.removeParticipant(transactionId);
                 return false;
             }
         }
@@ -164,6 +176,7 @@
                 e.printStackTrace(System.err);
                 return false;
             }
+            RestaurantParticipantBA.removeParticipant(transactionId);
             return false;
         }
 

Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantView.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantView.java	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/restaurant/RestaurantView.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -69,8 +69,6 @@
         jLabel9 = new javax.swing.JLabel();
         jLabelNPreparedSeats = new javax.swing.JLabel();
         jLabel2 = new javax.swing.JLabel();
-        jLabelNConfirmedSeats = new javax.swing.JLabel();
-        jLabel6 = new javax.swing.JLabel();
         jLabelNFreeSeats = new javax.swing.JLabel();
         jLabel7 = new javax.swing.JLabel();
         jButtonResetFields = new javax.swing.JButton();
@@ -123,14 +121,6 @@
         jLabel2.setText("Prepared,    ");
         jPanel2.add(jLabel2);
 
-        jLabelNConfirmedSeats.setText(Integer.toString(0));
-        jLabelNConfirmedSeats.setForeground(new java.awt.Color(0, 51, 204));
-        jLabelNConfirmedSeats.setFont(new java.awt.Font("Dialog", 1, 18));
-        jPanel2.add(jLabelNConfirmedSeats);
-
-        jLabel6.setText("Confirmed,    ");
-        jPanel2.add(jLabel6);
-
         jLabelNFreeSeats.setText(Integer.toString(restManager.getNFreeSeats()));
         jLabelNFreeSeats.setForeground(new java.awt.Color(0, 153, 0));
         jLabelNFreeSeats.setFont(new java.awt.Font("Dialog", 1, 18));
@@ -386,7 +376,6 @@
         jLabelNTotalSeats.setText(Integer.toString(restManager.getNTotalSeats()));
         jTextFieldNewNTotalSeats.setText(Integer.toString(restManager.getNTotalSeats()));
         jLabelNPreparedSeats.setText(Integer.toString(restManager.getNPreparedSeats()));
-        jLabelNConfirmedSeats.setText(Integer.toString(0));
         jLabelNFreeSeats.setText(Integer.toString(restManager.getNFreeSeats()));
         jLabelNBookedSeats.setText(Integer.toString(restManager.getNBookedSeats()));
 
@@ -435,8 +424,6 @@
     private javax.swing.JLabel jLabel9;
     private javax.swing.JLabel jLabelNPreparedSeats;
     private javax.swing.JLabel jLabel2;
-    private javax.swing.JLabel jLabelNConfirmedSeats;
-    private javax.swing.JLabel jLabel6;
     private javax.swing.JLabel jLabelNFreeSeats;
     private javax.swing.JLabel jLabel7;
     private javax.swing.JButton jButtonResetFields;

Added: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/state/ServiceStateConstants.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/state/ServiceStateConstants.java	                        (rev 0)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/state/ServiceStateConstants.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -0,0 +1,20 @@
+package com.jboss.jbosstm.xts.demo.services.state;
+
+public class ServiceStateConstants {
+    /**
+     * bit mask identifying no tx
+     */
+    public static final int TX_TYPE_NONE = 0;
+    /**
+     * bit mask identifying a WS-AT tx
+     */
+    public static final int TX_TYPE_AT = 1;
+    /**
+     * bit mask identifying a WS-BA tx
+     */
+    public static final int TX_TYPE_BA = 2;
+    /**
+     * bit mask identifying the union of both TX types
+     */
+    public static final int TX_TYPE_BOTH = TX_TYPE_AT | TX_TYPE_BA;
+}
\ No newline at end of file

Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/state/ServiceStateManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/state/ServiceStateManager.java	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/state/ServiceStateManager.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -1,8 +1,11 @@
 package com.jboss.jbosstm.xts.demo.services.state;
 
+import com.jboss.jbosstm.xts.demo.services.theatre.TheatreState;
+
 import java.io.*;
 import java.util.Enumeration;
 import java.util.Hashtable;
+import static com.jboss.jbosstm.xts.demo.services.state.ServiceStateConstants.*;
 
 /**
  * An abstract class extended by the web service manager classes which provides a simple capability
@@ -18,6 +21,7 @@
     protected ServiceStateManager()
     {
         transactions = new Hashtable<Object, T>();
+        restoreState();
     }
 
     protected void putState(Object txId, T state)
@@ -121,13 +125,11 @@
     }
 
     /**
-     * load and restore the current persisted service state at the same time re-establishing any shadow
-     * state and lock if appropiate. if no persisted state exists then return null.
-     *
-     * n.b. the subclass must ensure this is only called during initialization of the service
+     * load and install the current persisted service state at the same time re-establishing any shadow
+     * state and lock if appropriate. if no persisted state exists then set current state to null.
      */
 
-    protected T restoreState()
+    private void restoreState()
     {
         File file = new File(getStateFilename());
         File shadowFile = new File(getShadowStateFilename());
@@ -179,10 +181,15 @@
                 // we need the prepared tx id
                 shadowTxId = ois.readObject();
                 shadow = (T) ois.readObject();
+                // flag this prpeared state for deletion by default
+                // if we find out we need when processing recovery records it will be reset
+                rollbackPreparedTx = true;
             } catch (IOException e) {
                 System.out.println("error : unable to read shadow restaurant manager state " + e);
+                shadowTxId = null;
             } catch (ClassNotFoundException e) {
                 System.out.println("error : unknown class reading shadow restaurant manager state " + e);
+                shadowTxId = null;
             }
         }
 
@@ -210,7 +217,7 @@
             }
         }
 
-        return current;
+        currentState = current;
     }
 
     /**
@@ -225,33 +232,168 @@
         }
     }
 
+    /*****************************************************************************/
+    /* Recovery methods maintaining consistency of local and  WSAT/WSBA state    */
+    /*****************************************************************************/
     /**
-     * bit mask identifying no tx
+     * called by the AT and BA recovery modules to notify the manager that a participant associated with
+     * a specific AT or BA transaction has been recovered from a participant log record.
+     * @param txID
+     * @param txType
+     * @return true if there is prepared local state for this transaction which needs to be committed or
+     * rolled  back otherwise false
      */
-    public static final int TX_TYPE_NONE = 0;
+    public boolean recovered(Object txID, int txType)
+    {
+        // if the recovered transaction id matches the prepared tx we need to let the caller know
+        // that there is some local service state to be committed or rolled back. we also need to inhibit
+        // deletion at end of scan because it will get removed as recovery progresses.
+        if (isLockID(txID)) {
+            // we may have crashed after writing a participant record for an AT participant which has
+            // prepared, but not yet committed, local changes as part fo the AT 2 phase commit
+            //
+            // alternatively we may have crashed between prepare and commit of local changes for a
+            // BA participant which was in the middle of completing.
+            //
+            // in the first case the recovery process will roll the prepared chanegs forward or back
+            // when the coordinator sends either a COMMIT or an ABORT message.
+            //
+            // in the second case the recovery process will roll forward the local changes to reflect
+            // the fact that the participant has been logged i.e. it will effectively finish the
+            // COMPLETE operation. This will initiate sending of COMPLETED messages to the coordinator.
+            // If the activity  is still running the coordinator will respond with either CLOSE or
+            // COMPENSATE when the client terminates the activity. If the activity has already been
+            // cancelled the coordinator will send an invalid transaction fault and the recovery code
+            // ill ensure the changes are compensated.
+
+            rollbackPreparedTx = false;
+            return true;
+        }
+
+        // there is no local state associated with this transaction id so we are not interested
+        // in the participant. This can happen when the participant log record is for  a BA participant
+        // which has already completed
+
+        return false;
+    }
+
     /**
-     * bit mask identifying a WS-AT tx
+     * called at the end of the first recovery AT and BA scan to notify the manager that there are no
+     * more AT or BA participant recovery records to process. this allows the manager to automatically
+     * roll back local prepared state if it is not needed for subsequent recovery
+     *
+     * @param txType
      */
-    public static final int TX_TYPE_AT = 1;
+    public void recoveryScanCompleted(int txType)
+    {
+        completedScans |= txType;
+        
+        // if both AT and BA scans are completed and the prepared state is not needed for recovery then
+        // whatever transaction created it will have failed before reaching prepare/complete so we can
+        // safely roll back the local changes and unlock the service state. otherwise we leave the state
+        // locked until the recovery process rolls it forward or back.
+        if (completedScans == TX_TYPE_BOTH && rollbackPreparedTx) {
+            rollback(getLockID());
+        }
+    }
+
     /**
-     * bit mask identifying a WS-BA tx
+     * Prepare local state changes for the supplied transaction
+     *
+     * @param txID The transaction identifier
+     * @return true on success, false otherwise
      */
-    public static final int TX_TYPE_BA = 2;
+    public boolean prepare(Object txID)
+    {
+        // ensure that we have seen this transaction before
+        T childState = getState(txID);
+        if (childState == null) {
+            return false;
+        }
+        // we have a single monolithic state element which means that only one transaction can prepare
+        // at any given time. we lock this state at prepare by providing the txId as a locking id. it only
+        // gets unlocked when we reach commit or rollback. the equivalent to the lock in memory is the
+        // shadow state file on disk.
+        synchronized (this) {
+            while (isLocked()) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+            }
 
+            // check no other bookings have been committed
+
+            if (!currentState.isParentOf(childState)) {
+                removeState(txID);
+                return false;
+            }
+
+            // see if we need user confirmation
+
+            if (!confirmPrepare()) {
+                removeState(txID);
+                return false;
+            }
+
+            // ok, so lock the state against other prepare/commits
+
+            lock(txID);
+        }
+        // if we got here then no other changes have invalidated our booking and we have locked out
+        // further changes until commit or rollback occurs. we write the derived child state to the
+        // shadow state file before returning. if we crash after the write we will detect the shadow
+        // state at reboot and restore the lock.
+        try {
+            writeShadowState(txID, childState);
+            return true;
+        } catch (Exception e) {
+            clearShadowState(txID);
+            synchronized (this) {
+                removeState(txID);
+                unlock();
+            }
+            System.err.println("RestaurantManager.prepareSeats(): Error attempting to prepare transaction: " + e);
+            return false;
+        }
+    }
+
     /**
-     * bit mask identifying the union of both TX types
+     * commit local state changes for the supplied transaction
+     *
+     * @param txID
      */
-    public static final int TX_TYPE_BOTH = TX_TYPE_AT | TX_TYPE_BA;
+    public void commit(Object txID)
+    {
+        synchronized (this) {
+            // if there is a shadow state with this id then we need to copy the shadow state file over to the
+            // real state file. it may be that there is no shadow state because this is a repeated commit
+            // request. if so then we must have committed earlier so there is no harm done.
+            if (isLockID(txID)) {
+                commitShadowState(txID);
+                // update the current state with the prepared state.
+                currentState = getPreparedState();
+                unlock();
+            }
+            removeState(txID);
+        }
+    }
 
     /**
-     * method for use by the AT and BA recovery modules to notify that the initial recovery scan
-     * has completed. once both scans have been performed the manager can safely delete any
-     * remaining prepared state for which there is no corresponding participant recovery record
-     * @param scanType
+     * roll back local state changes for the supplied transaction
+     * @param txID
      */
-    public synchronized void recoveryScanCompleted(int scanType)
+    public void rollback(Object txID)
     {
-        completedScans |= scanType;
+        synchronized (this) {
+            removeState(txID);
+            if (isLockID(txID)) {
+                clearShadowState(txID);
+                unlock();
+            }
+            this.notifyAll();
+        }
     }
 
     /**
@@ -267,6 +409,20 @@
     public abstract String getShadowStateFilename();
 
     /**
+     * method called during prepare of local state changes allowing the user to force a prepare failue
+     * @return true if the prepare shoudl succeed and false if it should fail
+     */
+    public abstract boolean confirmPrepare();
+
+    /**
+     * the latest version of the service state which includes a version id
+     * this state object is always stored on disk in the current state file
+     * a prepared version of a derived child state may also exist on disk in the
+     * shadow state file
+     */
+    protected T currentState;
+
+    /**
      * flag used to indicate that a prepare is in progress. updates to restaurantState may not proceed
      * until this is false and even then only if the updated value for the restaurant state is derived
      * from the current state. changes to this field must be guarded by synchronizing on the manager instance.
@@ -284,5 +440,13 @@
      * when both scans have completed it is safe to roll back any remaining prepared state
      * changes since there can be no associated participant.
      */
-    protected int completedScans = 0;
+    protected int completedScans = TX_TYPE_NONE;
+
+    /**
+     * Flag which determines whether we have to roll back any prepared changes to the server. We roll back
+     * changes for an AT or BA participant if there is no associated log record because the participant never
+     * prepared or completed, respectively. If we see a log record for an AT participant we leave the prepared
+     * state behind since the AT participant has prepared and may still commit.
+     */
+    private boolean rollbackPreparedTx;
 }

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	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiManager.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -33,7 +33,7 @@
 import java.io.*;
 
 /**
- * The transactional application logic for the Taxi Service
+ * The application logic for the Taxi Service
  * <p/>
  * Manages taxi reservations. Knows nothing about Web Services.
  * <p/>
@@ -46,7 +46,7 @@
 public class TaxiManager implements Serializable
 {
     /*****************************************************************************/
-    /* Support for the Web Service API                                           */
+    /* Support for the Web Services                                              */
     /*****************************************************************************/
 
     /**
@@ -103,36 +103,25 @@
      * @param txID The transaction identifier
      * @return true on success, false otherwise
      */
-    public synchronized boolean prepareTaxi(Object txID)
+    public synchronized boolean prepare(Object txID)
     {
         // ensure that we have seen this transaction before
         Integer request = (Integer) transactions.get(txID);
         if (request == null)
         {
+            transactions.remove(txID);
             return false;
         }
         else
         {
             // see if we need user confirmation
 
-            if (!autoCommitMode) {
-                // need to wait for the user to decide whether to go ahead or not with this participant
-                isPreparationWaiting = true;
-                synchronized (preparation) {
-                    try {
-                        preparation.wait();
-                    } catch (InterruptedException e) {
-                        // ignore
-                    }
-                }
-                isPreparationWaiting = false;
-
-                // process the user decision
-                if (!isCommit) {
-                    return false;
-                }
+            if (!confirmPrepare()) {
+                transactions.remove(txID);
+                return false;
+            } else {
+                return true;
             }
-            return true;
         }
     }
 
@@ -141,7 +130,7 @@
      *
      * @param txID
      */
-    public synchronized void commitTaxi(Object txID)
+    public synchronized void commit(Object txID)
     {
         // just need to remove the transaction from the hash map
         transactions.remove(txID);
@@ -152,19 +141,34 @@
      *
      * @param txID
      */
-    public synchronized void rollbackTaxi(Object txID)
+    public synchronized void rollback(Object txID)
     {
         // just need to remove the transaction from the hash map
         transactions.remove(txID);
     }
 
     /**
-     * handle a recovery error by rolling back the changes associated with the transaction
-     * @param txID
+     * method called during prepare of local state changes allowing the user to force a prepare failue
+     * @return true if the prepare shoudl succeed and false if it should fail
      */
-    public void error(String txID)
+    public boolean confirmPrepare()
     {
-        rollbackTaxi(txID);
+        if (autoCommitMode) {
+            return true;
+        } else {
+            // need to wait for the user to decide whether to go ahead or not with this participant
+            isPreparationWaiting = true;
+            synchronized (preparation) {
+                try {
+                    preparation.wait();
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+            }
+            isPreparationWaiting = false;
+
+            return isCommit;
+        }
     }
 
     /*****************************************************************************/
@@ -239,31 +243,6 @@
     }
 
     /*****************************************************************************/
-    /* Recovery methods maintaining consistency of local and  WSAT/WSBA state    */
-    /*****************************************************************************/
-
-    /**
-     * called by the AT recovery module when an AT participant is recovered from a log record
-     */
-    public void recovered(TaxiParticipantAT participant)
-    {
-        // nothing needed here
-    }
-
-    /**
-     * called by the BA recovery module when an AT participant is recovered from a log record
-     */
-    public void recovered(TaxiParticipantBA participant)
-    {
-        // nothing needed here
-    }
-
-    public void recoveryScanCompleted(int txType)
-    {
-        // nothing needed here
-    }
-
-    /*****************************************************************************/
     /* Private implementation                                                    */
     /*****************************************************************************/
 

Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiParticipantAT.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiParticipantAT.java	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiParticipantAT.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -59,6 +59,14 @@
         this.txID = txID;
     }
 
+    /**
+     * accessor for participant transaction id
+     * @return the participant transaction id
+     */
+    public String getTxID() {
+        return txID;
+    }
+
     /************************************************************************/
     /* Durable2PCParticipant methods                                        */
     /************************************************************************/
@@ -79,7 +87,7 @@
 
         getTaxiView().addPrepareMessage("id:" + txID + ". Prepare called on participant: " + this.getClass().toString());
 
-        boolean success = getTaxiManager().prepareTaxi(txID);
+        boolean success = getTaxiManager().prepare(txID);
 
         // Log the outcome and map the return value from
         // the business logic to the appropriate Vote type.
@@ -114,7 +122,7 @@
 
         getTaxiView().addMessage("id:" + txID + ". Commit called on participant: " + this.getClass().toString());
 
-        getTaxiManager().commitTaxi(txID);
+        getTaxiManager().commit(txID);
 
         getTaxiView().addMessage("Taxi committed\n");
 
@@ -137,7 +145,7 @@
 
         getTaxiView().addMessage("id:" + txID + ". Rollback called on participant: " + this.getClass().toString());
 
-        getTaxiManager().rollbackTaxi(txID);
+        getTaxiManager().rollback(txID);
 
         getTaxiView().addMessage("Taxi booking cancelled\n");
 

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	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiParticipantBA.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -30,19 +30,29 @@
 package com.jboss.jbosstm.xts.demo.services.taxi;
 
 import com.arjuna.wst.*;
+import com.arjuna.wst11.BAParticipantManager;
+import com.arjuna.wst11.ConfirmCompletedParticipant;
 
 import java.io.Serializable;
+import java.util.HashMap;
 
 /**
  * An adapter class that exposes the TaxiManager transaction lifecycle
- * API as a WS-T Business Activity participant.
+ * API as a WS-T Coordinator CompletionBusiness Activity participant.
  * Also logs events to a TaxiView object.
  *
+ * The Taxi Service does not actually manage any persistent local state since
+ * it does not really matter if a taxi or the clients fail to turn up (there
+ * will always be another taxi or another client round the corner). It uses
+ * a participant which employs the coordinator completion protocol but only
+ * because this makes the demo more interesting.
+ *
  * @author Jonathan Halliday (jonathan.halliday at arjuna.com)
  * @version $Revision: 1.2 $
  */
 public class TaxiParticipantBA
-        implements BusinessAgreementWithParticipantCompletionParticipant,
+        implements BusinessAgreementWithCoordinatorCompletionParticipant,
+        ConfirmCompletedParticipant,
         Serializable
 {
     /************************************************************************/
@@ -60,12 +70,52 @@
         this.txID = txID;
     }
 
+    /**
+     * accessor for participant transaction id
+     * @return the participant transaction id
+     */
+    public String getTxID() {
+        return txID;
+    }
+
     /************************************************************************/
     /* BusinessAgreementWithParticipantCompletionParticipant methods        */
     /************************************************************************/
+
+    
+    public void complete() throws WrongStateException, SystemException {
+        // prepare and then complete all local changes
+        getTaxiView().addPrepareMessage("id:" + txID + ". Attempting to prepare taxi.");
+        getTaxiView().updateFields();
+        if (!getTaxiManager().prepare(txID))
+        {
+            // tell the participant manager we cannot complete. this will force the activity to fail
+            getTaxiView().addMessage("id:" + txID + ". Failed to prepare taxi. Cancelling.");
+            getTaxiView().updateFields();
+            BAParticipantManager participantManager = getManager(txID);
+            try
+            {
+                participantManager.cannotComplete();
+            }
+            catch (Exception e)
+            {
+                System.err.println("bookTaxi: 'cannotComplete' callback failed");
+                e.printStackTrace(System.err);
+            }
+            removeParticipant(txID);
+        }
+        else
+        {
+            // we just need to return here. the XTS implementation will call confirmComplete
+            // identifying whether or not to roll forward or roll back these prepared changes
+
+            getTaxiView().addMessage("id:" + txID + ". Taxi prepared");
+            getTaxiView().updateFields();
+        }
+    }
     /**
-     * The transaction has completed successfully. The participant previously
-     * informed the coordinator that it was ready to complete.
+     * The activity has ended successfully. The participant previously
+     * completed its local changes.
      *
      * @throws WrongStateException never in this implementation.
      * @throws SystemException never in this implementation.
@@ -81,6 +131,8 @@
         getTaxiView().addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
 
         getTaxiView().updateFields();
+
+        removeParticipant(txID);
     }
 
     /**
@@ -98,11 +150,13 @@
 
         System.out.println("TaxiParticipantBA.cancel");
 
-        getTaxiManager().rollbackTaxi(txID);
+        getTaxiManager().rollback(txID);
 
         getTaxiView().addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
 
         getTaxiView().updateFields();
+
+        removeParticipant(txID);
     }
 
     /**
@@ -126,6 +180,8 @@
         
         getTaxiView().addMessage("id:" + txID + ". Compensated participant: " + this.getClass().toString());
         getTaxiView().updateFields();
+
+        removeParticipant(txID);
     }
 
     public String status () throws SystemException
@@ -146,16 +202,88 @@
 
         getTaxiView().updateFields();
 
-        // tell the manager we had an error
+        // roll back any prepared local state
 
-        getTaxiManager().error(txID);
+        getTaxiManager().rollback(txID);
 
         getTaxiView().addMessage("id:" + txID + ". Notified error for participant: " + this.getClass().toString());
 
         getTaxiView().updateFields();
+
+        removeParticipant(txID);
     }
 
+
     /************************************************************************/
+    /* ConfirmCompletedParticipant methods                                  */
+    /************************************************************************/
+
+    /**
+     * method called to perform commit or rollback of prepared changes to the underlying manager state after
+     * the participant recovery record has been written
+     *
+     * @param confirmed true if the log record has been written and changes should be rolled forward and false
+     * if it has not been written and changes should be rolled back
+     */
+
+    public void confirmCompleted(boolean confirmed) {
+        if (confirmed) {
+            getTaxiView().addMessage("id:" + txID + ". Taxi committed");
+            getTaxiView().updateFields();
+            getTaxiManager().commit(txID);
+        } else {
+            getTaxiView().addMessage("id:" + txID + ". Taxi rolled back");
+            getTaxiView().updateFields();
+            getTaxiManager().rollback(txID);
+        }
+    }
+
+    /************************************************************************/
+    /* tracking active participants                                         */
+    /************************************************************************/
+    /**
+     * keep track of a participant
+     * @param txID
+     * @param participant
+     */
+    public static synchronized void recordParticipant(String txID, TaxiParticipantBA participant, com.arjuna.wst11.BAParticipantManager manager)
+    {
+        participants.put(txID, participant);
+        managers.put(txID, manager);
+    }
+
+    /**
+     * forget about a participant
+     * @param txID
+     * @param participant
+     */
+    public static synchronized TaxiParticipantBA removeParticipant(String txID)
+    {
+        managers.remove(txID);
+        return participants.remove(txID);
+    }
+
+    /**
+     * lookup a participant
+     * @param txID
+     * @return the participant
+     */
+    public static synchronized TaxiParticipantBA getParticipant(String txID)
+    {
+        return participants.get(txID);
+    }
+
+    /**
+     * lookup a participant manager
+     * @param txID
+     * @return the participant
+     */
+    public static synchronized com.arjuna.wst11.BAParticipantManager getManager(String txID)
+    {
+        return managers.get(txID);
+    }
+
+    /************************************************************************/
     /* private implementation                                               */
     /************************************************************************/
     /**
@@ -165,11 +293,20 @@
      */
     protected String txID;
 
-    public TaxiView getTaxiView() {
+    private TaxiView getTaxiView() {
         return TaxiView.getSingletonInstance();
     }
 
-    public TaxiManager getTaxiManager() {
+    private TaxiManager getTaxiManager() {
         return TaxiManager.getSingletonInstance();
     }
+
+    /**
+     * table of currently active participants
+     */
+    private static HashMap<String, TaxiParticipantBA> participants = new HashMap<String, TaxiParticipantBA>();
+    /**
+     * table of currently active participant managers
+     */
+    private static HashMap<String, com.arjuna.wst11.BAParticipantManager> managers = new HashMap<String, com.arjuna.wst11.BAParticipantManager>();
 }

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	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/taxi/TaxiServiceBA.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -90,10 +90,12 @@
 
         taxiView.addMessage("******************************");
 
-        taxiView.addPrepareMessage("id:" + transactionId.toString() + ". Received a taxi booking request");
+        taxiView.addMessage("id:" + transactionId.toString() + ". Received a taxi booking request");
         taxiView.updateFields();
 
-        if (taxiManager.knowsAbout(transactionId)) {
+        TaxiParticipantBA taxiParticipant = TaxiParticipantBA.getParticipant(transactionId);
+        BAParticipantManager participantManager;
+        if (taxiParticipant != null) {
             // hmm, this means we have already completed changes in this transaction and are awaiting a close
             //or compensate request. this service does not support repeated requests in the same activity so
             // we fail this request.
@@ -104,70 +106,27 @@
             return false;
         }
 
-        TaxiParticipantBA taxiParticipant = new TaxiParticipantBA(transactionId);
-        BAParticipantManager participantManager;
+        taxiParticipant = new TaxiParticipantBA(transactionId);
 
         // enlist the Participant for this service:
         try
         {
-            participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(taxiParticipant, "org.jboss.jbossts.xts-demo:taxiBA:" + new Uid().toString());
+            participantManager = activityManager.enlistForBusinessAgreementWithCoordinatorCompletion(taxiParticipant, "org.jboss.jbossts.xts-demo:taxiBA:" + new Uid().toString());
         }
         catch (Exception e)
         {
             taxiView.addMessage("id:" + transactionId + ". Participant enrolement failed");
-            taxiManager.rollbackTaxi(transactionId);
+            taxiManager.rollback(transactionId);
             System.err.println("bookTaxi: Participant enrolment failed");
             e.printStackTrace(System.err);
             return false;
         }
 
+        TaxiParticipantBA.recordParticipant(transactionId, taxiParticipant, participantManager);
+
         // invoke the backend business logic:
         taxiManager.bookTaxi(transactionId);
 
-        // this service employs the participant completion protocol which means it decides when it wants to
-        // commit local changes. so we prepare and commit those changes now. if any other participant fails
-        // or the client decides to cancel we can rely upon being told to compensate.
-
-        if (taxiManager.prepareTaxi(transactionId))
-        {
-            taxiView.addMessage("id:" + transactionId + ". Seats prepared, trying to commit and enlist compensation Participant");
-            taxiView.updateFields();
-
-            // it worked, so now we need a participant enlisted in case of compensation:
-
-            try
-            {
-                // tell the participant manager we have finished our work
-                // this will call back to the participant once the recovery record has been written
-                // allowing it to commit or roll back the restaurant manager
-                participantManager.completed();
-            }
-            catch (Exception e)
-            {
-                System.err.println("bookTaxi: 'completed' callback failed");
-                taxiManager.rollbackTaxi(transactionId);
-                e.printStackTrace(System.err);
-                return false;
-            }
-        }
-        else
-        {
-            taxiView.addMessage("id:" + transactionId + ". Failed to reserve taxi. Cancelling.");
-            taxiView.updateFields();
-            try
-            {
-                // tell the participant manager we cannot complete. this will force the activity to fail
-                participantManager.cannotComplete();
-            }
-            catch (Exception e)
-            {
-                System.err.println("bookSeats: 'cannotComplete' callback failed");
-                e.printStackTrace(System.err);
-                return false;
-            }
-            return false;
-        }
-
         taxiView.addMessage("Request complete\n");
         taxiView.updateFields();
 

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	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreManager.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -38,24 +38,23 @@
 import java.io.*;
 
 /**
- * The transactional application logic for the Theatre Service.
+ * The application logic for the Theatre Service.
  * <p/>
  * Stores and manages seating reservations.
  * <p/>
  * The manager extends class ServiceStateManager which implements a very simple
  * transactional resource manager. It gives the theatre manager the ability to
- * persist the web service state in a local disk file and to make transactional
- * updates to that persistent state. The unit of locking is the whole of the
+ * persist the web service state in a local disk file, to make transactional
+ * updates to that persistent state and to reload the saved state when the server
+ * is restarted, including loading a prepared but not yet committed shadow state
+ * left behind because of a server crash. The unit of locking is the whole of the
  * service state so although bookings can be attempted by concurrent transactions
  * only one such booking will commit, forcing other concurrent transactions to
  * roll back. Conflict detection is implemented using a simple versioning scheme.
  *
  * The theatre manager provides a book method allowing the web service endpoint to book
- * or unbook seats. It also exposes prepare, commit and rollback operations used by both
- * WSAT and WSBA participants to drive prepare, commit and rollback of changes to the
- * persistent state. Finally it exposes recovery logic used by the WSAT and WSBA recovery
- * modules to tie recovery of WSAT and WSBA participants to recovery and rollback of the
- * local service state.
+ * or unbook seats. It also provides getters which allow the GUI to  monitor the state
+ * of the service while transactions are in progress.
  *
  * @author Jonathan Halliday (jonathan.halliday at arjuna.com)
  * @author Andrew Dinn (adinn at redhat.com)
@@ -64,7 +63,7 @@
 public class TheatreManager extends ServiceStateManager<TheatreState>
 {
     /*****************************************************************************/
-    /* Support for the Web Service API                                           */
+    /* Support for the Web Services                                              */
     /*****************************************************************************/
 
     /**
@@ -101,161 +100,84 @@
             }
         }
 
-        if (theatreState.freeSeats[area] < nSeats ||
-                theatreState.bookedSeats[area] + nSeats > theatreState.totalSeats[area]) {
-            throw new WebServiceException("requested number of seats (" + nSeats + ") not available");
-        }
+        // see if we already have a derived state from booking a previous area
 
-        // create a state derived from the current state which reflects the new booking count
+        TheatreState childState = getState(txID);
 
-        TheatreState childState =  theatreState.derivedState();
+        if (childState != null) {
+            // see if we can extend the booking for this new area
+            if (childState.freeSeats[area] < nSeats ||
+                    childState.bookedSeats[area] + nSeats > childState.totalSeats[area]) {
+                throw new WebServiceException("requested number of seats (" + nSeats + ") not available");
+            }
 
-        // update the number of booked and free seats in the derived state
+            // update the number of booked and free seats in the derived state to reflect this request
 
-        childState.freeSeats[area] -= nSeats;
-        childState.bookedSeats[area] += nSeats;
+            childState.freeSeats[area] -= nSeats;
+            childState.bookedSeats[area] += nSeats;
+        } else {
+            // see if we can derive a new state from the current state which will satisfy this request
+            if (currentState.freeSeats[area] < nSeats ||
+                    currentState.bookedSeats[area] + nSeats > currentState.totalSeats[area]) {
+                throw new WebServiceException("requested number of seats (" + nSeats + ") not available");
+            }
 
-        // install this as the current transaction state
-        
-        putState(txID, childState);
-    }
+            childState = currentState.derivedState();
 
-    /**
-     * check whether we have already seen a web service request in a given transaction
-     */
+            // install this as the current transaction state
+            putState(txID, childState);
 
-    public boolean knowsAbout(Object txID)
-    {
-        return getState(txID) != null;
+            // update the number of booked and free seats in the derived state to reflect this request
+
+            childState.freeSeats[area] -= nSeats;
+            childState.bookedSeats[area] += nSeats;
+        }
     }
 
     /*****************************************************************************/
-    /* Support for the AT and BA Participant API implementation                  */
+    /* Implementation of inherited abstract state management API                 */
     /*****************************************************************************/
 
     /**
-     * Prepare local state changes for the supplied transaction
-     *
-     * @param txID The transaction identifier
-     * @return true on success, false otherwise
+     * method called during prepare of local state changes allowing the user to force a prepare failue
+     * @return true if the prepare shoudl succeed and false if it should fail
      */
-    public boolean prepareSeats(Object txID)
+    public boolean confirmPrepare()
     {
-        // ensure that we have seen this transaction before
-        TheatreState childState = getState(txID);
-        if (childState == null) {
-            return false;
-        }
-        // we have a single monolithic state element which means that only one transaction can prepare
-        // at any given time. we lock this state at prepare by providing the txId as a locking id. it only
-        // gets unlocked when we reach commit or rollback. the equivalent to the lock in memory is the
-        // shadow state file on disk.
-        synchronized (this) {
-            while (isLocked()) {
+        if (autoCommitMode) {
+            return true;
+        } else {
+            // need to wait for the user to decide whether to go ahead or not with this participant
+            isPreparationWaiting = true;
+            synchronized (preparation) {
                 try {
-                    wait();
+                    preparation.wait();
                 } catch (InterruptedException e) {
                     // ignore
                 }
             }
+            isPreparationWaiting = false;
 
-            // check no other bookings have been committed
-
-            if (!theatreState.isParentOf(childState)) {
-                removeState(txID);
-                return false;
-            }
-
-            // see if we need user confirmation
-
-            if (!autoCommitMode) {
-                // need to wait for the user to decide whether to go ahead or not with this participant
-                isPreparationWaiting = true;
-                synchronized (preparation) {
-                    try {
-                        preparation.wait();
-                    } catch (InterruptedException e) {
-                        // ignore
-                    }
-                }
-                isPreparationWaiting = false;
-
-                // process the user decision
-                if (!isCommit) {
-                    removeState(txID);
-                    return false;
-                }
-            }
-
-            // ok, so lock the state against other prepare/commits
-
-            lock(txID);
+            return isCommit;
         }
-        // if we got here then no other changes have invalidated our booking and we have locked out
-        // further changes until commit or rollback occurs. we write the derived child state to the
-        // shadow state file before returning. if we crash after the write we will detect the shadow
-        // state at reboot and restore the lock.
-        try {
-            writeShadowState(txID, childState);
-            return true;
-        } catch (Exception e) {
-            clearShadowState(txID);
-            synchronized (this) {
-                removeState(txID);
-                unlock();
-            }
-            System.err.println("RestaurantManager.prepareSeats(): Error attempting to prepare transaction: " + e);
-            return false;
-        }
     }
 
     /**
-     * commit local state changes for the supplied transaction
-     *
-     * @param txID
+     * identify the name of file used to store the current service state
+      * @return the name of the file used to store the current service state
      */
-    public void commitSeats(Object txID)
-    {
-        synchronized (this) {
-            // if there is a shadow state with this id then we need to copy the shadow state file over to the
-            // real state file. it may be that there is no shadow state because this is a repeated commit
-            // request. if so then we must have committed earlier so there is no harm done.
-            if (isLockID(txID)) {
-                commitShadowState(txID);
-                // update the current state with the prepared state.
-                theatreState = getPreparedState();
-                unlock();
-            }
-            removeState(txID);
-        }
+    public String getStateFilename() {
+        return STATE_FILENAME;
     }
 
     /**
-     * roll back local state changes for the supplied transaction
-     * @param txID
+     * identify the name of file used to store the shadow service state
+      * @return the name of the file used to store the shadow service state
      */
-    public void rollbackSeats(Object txID)
-    {
-        synchronized (this) {
-            removeState(txID);
-            if (isLockID(txID)) {
-                clearShadowState(txID);
-                unlock();
-            }
-            this.notifyAll();
-        }
+    public String getShadowStateFilename() {
+        return SHADOW_STATE_FILENAME;
     }
 
-    /**
-     * Handle BA error for a specific booking.
-     *
-     * @param txID The transaction identifier
-     */
-    public synchronized void error(Object txID)
-    {
-        rollbackSeats(txID);
-    }
-
     /*****************************************************************************/
     /* Accessors for the GUI to view and reset the service state                 */
     /*****************************************************************************/
@@ -280,7 +202,7 @@
 
         // undo all existing bookings
 
-        TheatreState resetState = theatreState.derivedState();
+        TheatreState resetState = currentState.derivedState();
 
         for (int area = 0; area < NUM_SEAT_AREAS; area++) {
             resetState.totalSeats[area] = DEFAULT_SEATING_CAPACITY;
@@ -300,7 +222,7 @@
 
         clearTransactions();
 
-        theatreState = resetState;
+        currentState = resetState;
     }
     /**
      * Get the number of free seats in the given area.
@@ -310,7 +232,7 @@
      */
     public int getNFreeSeats(int area)
     {
-        return theatreState.freeSeats[area];
+        return currentState.freeSeats[area];
     }
 
     /**
@@ -321,7 +243,7 @@
      */
     public int getNTotalSeats(int area)
     {
-        return theatreState.totalSeats[area];
+        return currentState.totalSeats[area];
     }
 
     /**
@@ -332,7 +254,7 @@
      */
     public int getNBookedSeats(int area)
     {
-        return theatreState.bookedSeats[area];
+        return currentState.bookedSeats[area];
     }
 
     /**
@@ -345,7 +267,7 @@
     {
         if (isLocked()) {
             TheatreState childState = getPreparedState();
-            return childState.bookedSeats[area] - theatreState.bookedSeats[area];
+            return childState.bookedSeats[area] - currentState.bookedSeats[area];
         } else {
             return 0;
         }
@@ -410,63 +332,6 @@
     }
 
     /*****************************************************************************/
-    /* Implementation of inherited abstract sate management API                  */
-    /*****************************************************************************/
-
-    /**
-     * identify the name of file used to store the current service state
-      * @return the name of the file used to store the current service state
-     */
-    public String getStateFilename() {
-        return STATE_FILENAME;
-    }
-
-    /**
-     * identify the name of file used to store the shadow service state
-      * @return the name of the file used to store the shadow service state
-     */
-    public String getShadowStateFilename() {
-        return SHADOW_STATE_FILENAME;
-    }
-
-    /*****************************************************************************/
-    /* Recovery methods maintaining consistency of local and  WSAT/WSBA state    */
-    /*****************************************************************************/
-
-    /**
-     * called by the AT recovery module when an AT participant is recovered from a log record
-     */
-    public void recovered(TheatreParticipantAT participant)
-    {
-        // if this AT participant matches the prepared TX id then we need to leave it prepared and locked
-        // at the end of scanning so it can be completed at commit time
-        if (isLockID(participant.txID)) {
-            rollbackPreparedTx = false;
-        }
-    }
-
-    /**
-     * called by the BA recovery module when an AT participant is recovered from a log record
-     */
-    public void recovered(TheatreParticipantBA participant)
-    {
-        // if this AT participant matches the prepared TX id then we roll it forward here by calling
-        // confirmCompleted so once again we don't need to roll back the prepared state
-        if (isLockID(participant.txID)) {
-            participant.confirmCompleted(true);
-            rollbackPreparedTx = false;
-        }
-    }
-
-    public void recoveryScanCompleted(int txType)
-    {
-        super.recoveryScanCompleted(txType);
-        if (completedScans == TX_TYPE_BOTH && rollbackPreparedTx) {
-            rollbackSeats(getLockID());
-        }
-    }
-
-    /*****************************************************************************/
     /* Private implementation                                                    */
     /*****************************************************************************/
 
@@ -498,45 +363,25 @@
     private boolean isCommit;
 
     /**
-     * Flag which determines whether we have to roll back any prepared changes to the server. We roll back
-     * changes for an AT or BA participant if there is no associated log record because the participant never
-     * prepared or completed, respectvely.. If we see a log record for an AT participant we leave the prepared
-     * state behind since it will
-     */
-    private boolean rollbackPreparedTx;
-
-    /**
-     * the latest version of the theatre state which includes a version id
-     * this state object is always stored on disk in the theatre state file
-     * a prepared version of a derived child state may also exist on disk in the
-     * theatre shadow state file
-     */
-    private TheatreState theatreState;
-
-    /**
      * Create and initialise a new TheatreManager instance.
      */
     private TheatreManager()
     {
-        TheatreState restoredState = restoreState();
-        if (restoredState == null) {
+        super();
+        if (currentState == null) {
             // we need to create a new initial state and persist it to disk
-            restoredState = TheatreState.initialState();
+            currentState = TheatreState.initialState();
             Object txId = "initialisation-transaction-" + System.currentTimeMillis();
             try {
-                writeShadowState(txId, restoredState);
+                writeShadowState(txId, currentState);
                 commitShadowState(txId);
             } catch (IOException e) {
                 clearShadowState(txId);
                 System.out.println("error : unable to initialise theatre manager state " + e);
             }
         }
-        theatreState = restoredState;
 
         preparation = new Object();
-        // we will roll back any locally prepared changes to web service state unless we discover that
-        // they are needed during recovery
-        rollbackPreparedTx = isLocked();
 
         isCommit = true;
         autoCommitMode = true;

Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreParticipantAT.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreParticipantAT.java	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreParticipantAT.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -32,6 +32,7 @@
 import com.arjuna.wst.*;
 
 import java.io.Serializable;
+import java.util.HashMap;
 
 /**
  * An adapter class that exposes the TheatreManager transaction lifecycle
@@ -52,13 +53,25 @@
      *
      * @param txID uniq id String for the transaction instance.
      */
-    public TheatreParticipantAT(String txID)
+    public TheatreParticipantAT(String txID, int[] bookings)
     {
         // we need to save the txID for later use when calling
         // business logic methods in the theatreManger.
         this.txID = txID;
+        // we need to remember which seating areas have already been booked
+        this.bookings = bookings;
+        // we may invalidate the participant later if something goes wrong
+        this.valid = true;
     }
 
+    /**
+     * accessor for participant transaction id
+     * @return the participant transaction id
+     */
+    public String getTxID() {
+        return txID;
+    }
+
     /************************************************************************/
     /* Durable2PCParticipant methods                                        */
     /************************************************************************/
@@ -79,7 +92,7 @@
 
         getTheatreView().addPrepareMessage("id:" + txID + ". Prepare called on participant: " + this.getClass().toString());
 
-        boolean success = getTheatreManager().prepareSeats(txID);
+        boolean success = getTheatreManager().prepare(txID);
 
         // Log the outcome and map the return value from
         // the business logic to the appropriate Vote type.
@@ -115,7 +128,7 @@
 
         getTheatreView().addMessage("id:" + txID + ". Commit called on participant: " + this.getClass().toString());
 
-        getTheatreManager().commitSeats(txID);
+        getTheatreManager().commit(txID);
 
         getTheatreView().addMessage("Theatre tickets committed\n");
 
@@ -138,7 +151,7 @@
 
         getTheatreView().addMessage("id:" + txID + ". Rollback called on participant: " + this.getClass().toString());
 
-        getTheatreManager().rollbackSeats(txID);
+        getTheatreManager().rollback(txID);
 
         getTheatreView().addMessage("Theatre booking cancelled\n");
 
@@ -156,6 +169,57 @@
     }
 
     /************************************************************************/
+    /* tracking active participants                                         */
+    /************************************************************************/
+    /**
+     * keep track of a participant
+     * @param txID
+     * @param participant
+     */
+    public static synchronized void recordParticipant(String txID, TheatreParticipantAT participant)
+    {
+        participants.put(txID, participant);
+    }
+
+    /**
+     * forget about a participant
+     * @param txID
+     * @param participant
+     */
+    public static synchronized TheatreParticipantAT removeParticipant(String txID)
+    {
+        return participants.remove(txID);
+    }
+
+    /**
+     * lookup a participant
+     * @param txID
+     * @return the participant
+     */
+    public static synchronized TheatreParticipantAT getParticipant(String txID)
+    {
+        return participants.get(txID);
+    }
+
+    /**
+     * mark a participant as invalid
+     */
+    public void invalidate()
+    {
+        valid = false;
+    }
+
+    /**
+     * check if a participant is invalid
+     *
+     * @return
+     */
+    public boolean isValid()
+    {
+        return valid;
+    }
+
+    /************************************************************************/
     /* private implementation                                               */
     /************************************************************************/
     /**
@@ -165,11 +229,27 @@
      */
     protected String txID;
 
-    public TheatreView getTheatreView() {
+    /**
+     * array containing bookings for each of the seating areas. each area is booked in its own
+     * service request but we need this info in order to be able to detect repeated bookings.
+     */
+    protected int[] bookings;
+    
+    /**
+     * this is true by default but we invalidate the participant if the client makes invalid requests
+     */
+    protected boolean valid;
+    
+    private TheatreView getTheatreView() {
         return TheatreView.getSingletonInstance();
     }
 
-    public TheatreManager getTheatreManager() {
+    private TheatreManager getTheatreManager() {
         return TheatreManager.getSingletonInstance();
     }
+
+    /**
+     * table of currently active participants
+     */
+    private static HashMap<String, TheatreParticipantAT> participants = new HashMap<String,  TheatreParticipantAT>();
 }

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	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreParticipantBA.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -30,25 +30,27 @@
 package com.jboss.jbosstm.xts.demo.services.theatre;
 
 import com.arjuna.wst.*;
+import com.arjuna.wst11.BAParticipantManager;
 import com.arjuna.wst11.ConfirmCompletedParticipant;
-
+import static com.jboss.jbosstm.xts.demo.services.theatre.TheatreConstants.*;
 import java.io.Serializable;
+import java.util.HashMap;
 
 /**
  * An adapter class that exposes the TheatreManager transaction lifecycle
- * API as a WS-T Business Activity participant.
+ * API as a WS-T Coordinator Completion Business Activity participant.
  * Also logs events to a TheatreView object.
  *
  * @author Jonathan Halliday (jonathan.halliday at arjuna.com)
  * @version $Revision: 1.3 $
  */
 public class TheatreParticipantBA implements
-        BusinessAgreementWithParticipantCompletionParticipant,
+        BusinessAgreementWithCoordinatorCompletionParticipant,
         ConfirmCompletedParticipant,
         Serializable
 {
     /************************************************************************/
-    /* public methods                                                       */
+    /* public methods                                                   */
     /************************************************************************/
     /**
      * Participant instances are related to business method calls
@@ -56,15 +58,26 @@
      *
      * @param txID       uniq id String for the transaction instance.
      */
-    public TheatreParticipantBA(String txID, int how_many, int which_area)
+    public TheatreParticipantBA(String txID, int[] bookings)
     {
         // we need to save the txID for later use when logging
+        // and the seat count and seat area for use during compensation
         this.txID = txID;
-        this.seatCount = how_many;
-        this.seatingArea = which_area;
+        this.bookings = bookings;
     }
 
     /**
+     * accessor for participant transaction id
+     * @return the participant transaction id
+     */
+    public String getTxID() {
+        return txID;
+    }
+
+    /************************************************************************/
+    /* BusinessAgreementWithCoordinatorCompletionParticipant methods        */
+    /************************************************************************/
+    /**
      * The transaction has completed successfully. The participant previously
      * informed the coordinator that it was ready to complete.
      *
@@ -72,9 +85,37 @@
      * @throws SystemException never in this implementation.
      */
 
-    /************************************************************************/
-    /* BusinessAgreementWithParticipantCompletionParticipant methods        */
-    /************************************************************************/
+    public void complete() throws WrongStateException, SystemException
+    {
+        BAParticipantManager participantManager = managers.get(txID);
+        getTheatreView().addPrepareMessage("id:" + txID + ". Attempting to prepare seats.");
+        getTheatreView().updateFields();
+        if (!getTheatreManager().prepare(txID))
+        {
+            // tell the participant manager we cannot complete. this will force the activity to fail
+            getTheatreView().addMessage("id:" + txID + ". Failed to reserve seats. Cancelling.");
+            getTheatreView().updateFields();
+            try
+            {
+                participantManager.cannotComplete();
+            }
+            catch (Exception e)
+            {
+                System.err.println("bookSeats: 'cannotComplete' callback failed");
+                e.printStackTrace(System.err);
+            }
+            removeParticipant(txID);
+        }
+        else
+        {
+            // we just need to return here. the XTS implementation will call confirmComplete
+            // identifying whether or not to roll forward or roll back these prepared changes
+
+            getTheatreView().addMessage("id:" + txID + ". Seats prepared");
+            getTheatreView().updateFields();
+        }
+    }
+
     public void close() throws WrongStateException, SystemException
     {
         // nothing to do here as the seats are already booked
@@ -84,6 +125,8 @@
         getTheatreView().addMessage("id:" + txID + ". Close called on participant: " + this.getClass());
 
         getTheatreView().updateFields();
+
+        removeParticipant(txID);
     }
 
     /**
@@ -101,11 +144,13 @@
 
         System.out.println("TheatreParticipantBA.cancel");
 
-        getTheatreManager().rollbackSeats(txID);
+        getTheatreManager().rollback(txID);
 
         getTheatreView().addMessage("id:" + txID + ". Cancel called on participant: " + this.getClass().toString());
 
         getTheatreView().updateFields();
+
+        removeParticipant(txID);
     }
 
     /**
@@ -125,23 +170,31 @@
 
         getTheatreView().updateFields();
 
-        // we perform the compensation by preparing and then committing a change which
-        // decrements the bookings
+        // we perform the compensation by preparing and then committing local changes which
+        // decrement the booked seat counts
 
         String compensationTxID = txID + "-compensation";
 
-        getTheatreManager().bookSeats(compensationTxID, -seatCount, seatingArea);
+        for (int seatingArea = 0; seatingArea < NUM_SEAT_AREAS; seatingArea++) {
+            if (bookings[seatingArea] != 0) {
+                getTheatreManager().bookSeats(compensationTxID, -bookings[seatingArea], seatingArea);
+            }
+        }
 
-        if (!getTheatreManager().prepareSeats(compensationTxID)) {
+        if (!getTheatreManager().prepare(compensationTxID)) {
             getTheatreView().addMessage("id:" + txID + ". Failed to compensate participant: " + this.getClass().toString());
 
+            removeParticipant(txID);
+
             throw new FaultedException("Failed to compensate participant " + txID);
         }
-        getTheatreManager().commitSeats(compensationTxID);
+        getTheatreManager().commit(compensationTxID);
 
         getTheatreView().addMessage("id:" + txID + ". Compensated participant: " + this.getClass().toString());
 
         getTheatreView().updateFields();
+
+        removeParticipant(txID);
     }
     
     public String status()
@@ -151,7 +204,8 @@
 
     public void unknown() throws SystemException
     {
-        // used for calbacks during crash recovery. This impl is not recoverable
+
+        removeParticipant(txID);
     }
 
     public void error() throws SystemException
@@ -162,13 +216,15 @@
 
         getTheatreView().updateFields();
 
-        // tell the manager we had an error
+        // roll back any prepared local state
 
-        getTheatreManager().error(txID);
+        getTheatreManager().rollback(txID);
 
         getTheatreView().addMessage("id:" + txID + ". Notified error for participant: " + this.getClass().toString());
 
         getTheatreView().updateFields();
+
+        removeParticipant(txID);
     }
 
     /************************************************************************/
@@ -185,13 +241,62 @@
 
     public void confirmCompleted(boolean confirmed) {
         if (confirmed) {
-            getTheatreManager().commitSeats(txID);
+            getTheatreManager().commit(txID);
+            getTheatreView().addMessage("id:" + txID + ". Seats committed");
+            getTheatreView().updateFields();
         } else {
-            getTheatreManager().rollbackSeats(txID);
+            getTheatreManager().rollback(txID);
+            getTheatreView().addMessage("id:" + txID + ". Seats rolled back");
+            getTheatreView().updateFields();
         }
     }
 
     /************************************************************************/
+    /* tracking active participants                                         */
+    /************************************************************************/
+    /**
+     * keep track of a participant
+     * @param txID
+     * @param participant
+     */
+    public static synchronized void recordParticipant(String txID, TheatreParticipantBA participant, BAParticipantManager manager)
+    {
+        participants.put(txID, participant);
+        managers.put(txID, manager);
+    }
+
+    /**
+     * forget about a participant
+     * @param txID
+     * @param participant
+     */
+    public static synchronized TheatreParticipantBA removeParticipant(String txID)
+    {
+        managers.remove(txID);
+        return participants.remove(txID);
+    }
+
+    /**
+     * lookup a participant
+     * @param txID
+     * @return the participant
+     */
+    public static synchronized TheatreParticipantBA getParticipant(String txID)
+    {
+        return participants.get(txID);
+    }
+
+    /**
+     * lookup a participant manager
+     * @param txID
+     * @return the participant
+     */
+    public static synchronized BAParticipantManager getManager(String txID)
+    {
+        return managers.get(txID);
+    }
+
+    /************************************************************************/
     /* private implementation                                               */
     /************************************************************************/
     /**
@@ -202,21 +307,25 @@
     protected String txID;
 
     /**
-     * Copy of business state information, may be needed during compensation.
+     * array containing bookings for each of the seating areas. each area is booked in its own
+     * service request but we need this info in order to be able to detect repeated bookings.
      */
-    protected int seatCount;
+    protected int[] bookings;
 
-    /**
-     * Copy of business state information, may be needed during compensation.
-     */
-    protected int seatingArea;
-
-    public TheatreView getTheatreView() {
+    private TheatreView getTheatreView() {
         return TheatreView.getSingletonInstance();
     }
 
-    public TheatreManager getTheatreManager() {
+    private TheatreManager getTheatreManager() {
         return TheatreManager.getSingletonInstance();
     }
 
+    /**
+     * table of currently active participants
+     */
+    private static HashMap<String, TheatreParticipantBA> participants = new HashMap<String, TheatreParticipantBA>();
+    /**
+     * table of currently active participant managers
+     */
+    private static HashMap<String, BAParticipantManager> managers = new HashMap<String, BAParticipantManager>();
 }

Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreServiceAT.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreServiceAT.java	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreServiceAT.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -33,7 +33,7 @@
 import com.arjuna.mw.wst11.TransactionManagerFactory;
 import com.arjuna.mw.wst11.UserTransactionFactory;
 import com.jboss.jbosstm.xts.demo.theatre.ITheatreServiceAT;
-import com.jboss.jbosstm.xts.demo.services.recovery.DemoATRecoveryModule;
+import static com.jboss.jbosstm.xts.demo.services.theatre.TheatreConstants.*;
 
 import javax.jws.WebService;
 import javax.jws.WebParam;
@@ -80,13 +80,32 @@
             transactionId = UserTransactionFactory.userTransaction().toString();
             System.out.println("TheatreServiceAT transaction id =" + transactionId);
 
-            if (!theatreManager.knowsAbout(transactionId))
+            TheatreParticipantAT theatreParticipant = TheatreParticipantAT.getParticipant(transactionId);
+            if (theatreParticipant == null)
             {
                 System.out.println("theatreService - enrolling...");
                 // enlist the Participant for this service:
-                TheatreParticipantAT theatreParticipant = new TheatreParticipantAT(transactionId);
+                int[] bookings = new int[NUM_SEAT_AREAS];
+                bookings[which_area] = how_many;
+                theatreParticipant = new TheatreParticipantAT(transactionId, bookings);
                 TransactionManagerFactory.transactionManager().enlistForDurableTwoPhase(theatreParticipant, "org.jboss.jbossts.xts-demo:theatreAT:" + new Uid().toString());
+                TheatreParticipantAT.recordParticipant(transactionId, theatreParticipant);
             }
+            else if (theatreParticipant.isValid() && theatreParticipant.bookings[which_area] == 0)
+            {
+                theatreParticipant.bookings[which_area] = how_many;
+            } else {
+                // this service does not support repeated bookings in the same transaction
+                // so mark the participant as invalid
+                theatreView.addMessage("id:" + transactionId + ". Participant seats already booked!");
+                theatreView.updateFields();
+                System.err.println("bookSeats: request failed");
+                // this ensures we do not try later to prepare the participant
+                theatreParticipant.invalidate();
+                // throw away any local changes previously made on behalf of the participant
+                theatreManager.rollback(transactionId);
+                return;
+            }
         }
         catch (Exception e)
         {

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	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreServiceBA.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -35,6 +35,7 @@
 import com.arjuna.wst11.BAParticipantManager;
 import com.arjuna.wst.SystemException;
 import com.jboss.jbosstm.xts.demo.theatre.ITheatreServiceBA;
+import static com.jboss.jbosstm.xts.demo.services.theatre.TheatreConstants.*;
 
 import javax.jws.*;
 import javax.jws.soap.SOAPBinding;
@@ -43,6 +44,15 @@
  * An adapter class that exposes the TheatreManager business API as a
  * transactional Web Service. Also logs events to a TheatreView object.
  *
+ * The BA Theatre Service allows the client to make up to three bookings in any given transaction,
+ * one for each seating area. So, this means that it cannot know when the client has finished
+ * making service requests. Hence it uses a participant which implements the coordinator
+ * completion protocol. When the client closes the activity the coordinator sends a COMPLETED
+ * message to the BA participant before sending the CLOSE message. If the client cancels the
+ * activity then the coordinator only has to send a CANCEL message. The service may still be told
+ * to COMPENSATE if it successfully completes and then some other service (e.g. the Taxi service)
+ * fails to complete.
+ *
  * @author Jonathan Halliday (jonathan.halliday at arjuna.com)
  * @version $Revision: 1.5 $
  */
@@ -93,82 +103,56 @@
 
         theatreView.addMessage("******************************");
 
-        theatreView.addPrepareMessage("id:" + transactionId + ". Received a theatre booking request for " + how_many + " seats in area " + which_area);
+        theatreView.addMessage("id:" + transactionId + ". Received a theatre booking request for " + how_many + " seats in area " + which_area);
         theatreView.updateFields();
 
-        if (theatreManager.knowsAbout(transactionId)) {
-            // hmm, this means we have already completed changes in this transaction and are awaiting a close
-            //or compensate request. this service does not support repeated requests in the same activity so
-            // we fail this request.
-
-            theatreView.addMessage("id:" + transactionId + ". Participant already enrolled!");
-            theatreView.updateFields();
-            System.err.println("bookSeats: request failed");
-            return false;
-        }
-
-        TheatreParticipantBA theatreParticipant = new TheatreParticipantBA(transactionId, how_many, which_area);
+        TheatreParticipantBA theatreParticipant = TheatreParticipantBA.getParticipant(transactionId);
         BAParticipantManager participantManager;
 
-        // enlist the Participant for this service:
-        try
-        {
-            participantManager = activityManager.enlistForBusinessAgreementWithParticipantCompletion(theatreParticipant, "org.jboss.jbossts.xts-demo:theatreBA:" + new Uid().toString());
-        }
-        catch (Exception e)
-        {
-            theatreView.addMessage("id:" + transactionId + ". Participant enrolement failed");
-            theatreManager.rollbackSeats(transactionId);
-            System.err.println("bookSeats: Participant enrolement failed");
-            e.printStackTrace(System.err);
-            return false;
-        }
-
-        // invoke the backend business logic:
-        theatreManager.bookSeats(transactionId, how_many, which_area);
-
-        // this service employs the participant completion protocol which means it decides when it wants to
-        // commit local changes. so we prepare and commit those changes now. if any other participant fails
-        // or the client decides to cancel we can rely upon being told to compensate.
-
-        if (theatreManager.prepareSeats(transactionId))
-        {
-            theatreView.addMessage("id:" + transactionId + ". Seats prepared, trying to commit and enlist compensation Participant");
-            theatreView.updateFields();
-
+        if (theatreParticipant == null) {
+            int[] bookings = new int[NUM_SEAT_AREAS];
+            bookings[which_area] = how_many;
+            theatreParticipant = new TheatreParticipantBA(transactionId, bookings);
+            // enlist the Participant for this service:
             try
             {
-                // tell the participant manager we have finished our work
-                // this will call back to the participant once a compensation recovery record has been written
-                // allowing it to commit or roll back the theatre manager
-                participantManager.completed();
+                participantManager = activityManager.enlistForBusinessAgreementWithCoordinatorCompletion(theatreParticipant, "org.jboss.jbossts.xts-demo:theatreBA:" + new Uid().toString());
+                TheatreParticipantBA.recordParticipant(transactionId, theatreParticipant, participantManager);
             }
             catch (Exception e)
             {
-                System.err.println("bookSeats: 'completed' callback failed");
-                theatreManager.rollbackSeats(transactionId);
+                theatreView.addMessage("id:" + transactionId + ". Participant enrolement failed");
+                System.err.println("bookSeats: Participant enrolement failed");
                 e.printStackTrace(System.err);
                 return false;
             }
-        }
-        else
-        {
-            theatreView.addMessage("id:" + transactionId + ". Failed to reserve seats. Cancelling.");
-            theatreView.updateFields();
-            try
-            {
-                // tell the participant manager we cannot complete. this will force the activity to fail
+        } else if (theatreParticipant.bookings[which_area] == 0) {
+            theatreParticipant.bookings[which_area] = how_many;
+        } else {
+            // hmm, this means we have already completed changes in this transaction and are awaiting a close
+            //or compensate request. this service does not support repeated requests in the same activity so
+            // we ensure the activity cannot continue by calling cannotComplete and also roll back
+            // any local changes
+            participantManager = TheatreParticipantBA.getManager(transactionId);
+            try {
                 participantManager.cannotComplete();
-            }
-            catch (Exception e)
-            {
+            } catch (Exception e) {
                 System.err.println("bookSeats: 'cannotComplete' callback failed");
                 e.printStackTrace(System.err);
-                return false;
             }
+            theatreManager.rollback(transactionId);
+            TheatreParticipantBA.removeParticipant(transactionId);
+            theatreView.addMessage("id:" + transactionId + ". repeat booking for area " + which_area);
+            theatreView.updateFields();
+            System.err.println("bookSeats: request failed");
             return false;
         }
 
+        // invoke the backend business logic:
+        theatreManager.bookSeats(transactionId, how_many, which_area);
+
+        // this service employs the coordinator completion protocol which means we don't actually prepare and
+        // commit these changes until the coordinator sends a complete request through.
         theatreView.addMessage("Request complete\n");
         theatreView.updateFields();
 

Modified: labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreView.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreView.java	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/theatre/TheatreView.java	2010-09-24 14:43:12 UTC (rev 35272)
@@ -73,15 +73,15 @@
         jLabel30 = new javax.swing.JLabel();
         jLabel25 = new javax.swing.JLabel();
         jLabelNBookedSeatsCircle = new javax.swing.JLabel();
-        jLabelNConfirmedSeatsCircle = new javax.swing.JLabel();
+        jLabelNPreparedSeatsCircle = new javax.swing.JLabel();
         jLabelNFreeSeatsCircle = new javax.swing.JLabel();
         jLabel35 = new javax.swing.JLabel();
         jLabelNBookedSeatsStalls = new javax.swing.JLabel();
-        jLabelNConfirmedSeatsStalls = new javax.swing.JLabel();
+        jLabelNPreparedSeatsStalls = new javax.swing.JLabel();
         jLabelNFreeSeatsStalls = new javax.swing.JLabel();
         jLabel28 = new javax.swing.JLabel();
         jLabelNBookedSeatsBalcony = new javax.swing.JLabel();
-        jLabelNConfirmedSeatsBalcony = new javax.swing.JLabel();
+        jLabelNPreparedSeatsBalcony = new javax.swing.JLabel();
         jLabelNFreeSeatsBalcony = new javax.swing.JLabel();
         jLabel34 = new javax.swing.JLabel();
         jButtonResetFields = new javax.swing.JButton();
@@ -127,7 +127,7 @@
         jLabel3.setForeground(java.awt.Color.gray);
         jPanel1.add(jLabel3);
 
-        jLabel29.setText("confirmed, ");
+        jLabel29.setText("prepared, ");
         jLabel29.setForeground(new java.awt.Color(0, 51, 204));
         jPanel1.add(jLabel29);
 
@@ -144,10 +144,10 @@
         jLabelNBookedSeatsCircle.setFont(new java.awt.Font("Dialog", 0, 14));
         jPanel1.add(jLabelNBookedSeatsCircle);
 
-        jLabelNConfirmedSeatsCircle.setText(Integer.toString(0));
-        jLabelNConfirmedSeatsCircle.setForeground(new java.awt.Color(0, 51, 204));
-        jLabelNConfirmedSeatsCircle.setFont(new java.awt.Font("Dialog", 0, 14));
-        jPanel1.add(jLabelNConfirmedSeatsCircle);
+        jLabelNPreparedSeatsCircle.setText(Integer.toString(theatreManager.getNPreparedSeats(CIRCLE)));
+        jLabelNPreparedSeatsCircle.setForeground(new java.awt.Color(0, 51, 204));
+        jLabelNPreparedSeatsCircle.setFont(new java.awt.Font("Dialog", 0, 14));
+        jPanel1.add(jLabelNPreparedSeatsCircle);
 
         jLabelNFreeSeatsCircle.setText(Integer.toString(theatreManager.getNFreeSeats(CIRCLE)));
         jLabelNFreeSeatsCircle.setForeground(new java.awt.Color(0, 153, 0));
@@ -163,10 +163,10 @@
         jLabelNBookedSeatsStalls.setFont(new java.awt.Font("Dialog", 0, 14));
         jPanel1.add(jLabelNBookedSeatsStalls);
 
-        jLabelNConfirmedSeatsStalls.setText(Integer.toString(0));
-        jLabelNConfirmedSeatsStalls.setForeground(new java.awt.Color(0, 51, 204));
-        jLabelNConfirmedSeatsStalls.setFont(new java.awt.Font("Dialog", 0, 14));
-        jPanel1.add(jLabelNConfirmedSeatsStalls);
+        jLabelNPreparedSeatsStalls.setText(Integer.toString(theatreManager.getNPreparedSeats(STALLS)));
+        jLabelNPreparedSeatsStalls.setForeground(new java.awt.Color(0, 51, 204));
+        jLabelNPreparedSeatsStalls.setFont(new java.awt.Font("Dialog", 0, 14));
+        jPanel1.add(jLabelNPreparedSeatsStalls);
 
         jLabelNFreeSeatsStalls.setText(Integer.toString(theatreManager.getNFreeSeats(STALLS)));
         jLabelNFreeSeatsStalls.setForeground(new java.awt.Color(0, 153, 0));
@@ -182,10 +182,10 @@
         jLabelNBookedSeatsBalcony.setFont(new java.awt.Font("Dialog", 0, 14));
         jPanel1.add(jLabelNBookedSeatsBalcony);
 
-        jLabelNConfirmedSeatsBalcony.setText(Integer.toString(0));
-        jLabelNConfirmedSeatsBalcony.setForeground(new java.awt.Color(51, 0, 204));
-        jLabelNConfirmedSeatsBalcony.setFont(new java.awt.Font("Dialog", 0, 14));
-        jPanel1.add(jLabelNConfirmedSeatsBalcony);
+        jLabelNPreparedSeatsBalcony.setText(Integer.toString(theatreManager.getNPreparedSeats(BALCONY)));
+        jLabelNPreparedSeatsBalcony.setForeground(new java.awt.Color(51, 0, 204));
+        jLabelNPreparedSeatsBalcony.setFont(new java.awt.Font("Dialog", 0, 14));
+        jPanel1.add(jLabelNPreparedSeatsBalcony);
 
         jLabelNFreeSeatsBalcony.setText(Integer.toString(theatreManager.getNFreeSeats(BALCONY)));
         jLabelNFreeSeatsBalcony.setForeground(new java.awt.Color(0, 153, 0));
@@ -509,9 +509,9 @@
         jLabelNBookedSeatsCircle.setText(Integer.toString(theatreManager.getNBookedSeats(CIRCLE)));
         jLabelNBookedSeatsStalls.setText(Integer.toString(theatreManager.getNBookedSeats(STALLS)));
         jLabelNBookedSeatsBalcony.setText(Integer.toString(theatreManager.getNBookedSeats(BALCONY)));
-        jLabelNConfirmedSeatsCircle.setText(Integer.toString(0));
-        jLabelNConfirmedSeatsStalls.setText(Integer.toString(0));
-        jLabelNConfirmedSeatsBalcony.setText(Integer.toString(0));
+        jLabelNPreparedSeatsCircle.setText(Integer.toString(theatreManager.getNPreparedSeats(CIRCLE)));
+        jLabelNPreparedSeatsStalls.setText(Integer.toString(theatreManager.getNPreparedSeats(STALLS)));
+        jLabelNPreparedSeatsBalcony.setText(Integer.toString(theatreManager.getNPreparedSeats(BALCONY)));
         jLabelNFreeSeatsCircle.setText(Integer.toString(theatreManager.getNFreeSeats(CIRCLE)));
         jLabelNFreeSeatsStalls.setText(Integer.toString(theatreManager.getNFreeSeats(STALLS)));
         jLabelNFreeSeatsBalcony.setText(Integer.toString(theatreManager.getNFreeSeats(BALCONY)));
@@ -562,15 +562,15 @@
     private javax.swing.JLabel jLabel30;
     private javax.swing.JLabel jLabel25;
     private javax.swing.JLabel jLabelNBookedSeatsCircle;
-    private javax.swing.JLabel jLabelNConfirmedSeatsCircle;
+    private javax.swing.JLabel jLabelNPreparedSeatsCircle;
     private javax.swing.JLabel jLabelNFreeSeatsCircle;
     private javax.swing.JLabel jLabel35;
     private javax.swing.JLabel jLabelNBookedSeatsStalls;
-    private javax.swing.JLabel jLabelNConfirmedSeatsStalls;
+    private javax.swing.JLabel jLabelNPreparedSeatsStalls;
     private javax.swing.JLabel jLabelNFreeSeatsStalls;
     private javax.swing.JLabel jLabel28;
     private javax.swing.JLabel jLabelNBookedSeatsBalcony;
-    private javax.swing.JLabel jLabelNConfirmedSeatsBalcony;
+    private javax.swing.JLabel jLabelNPreparedSeatsBalcony;
     private javax.swing.JLabel jLabelNFreeSeatsBalcony;
     private javax.swing.JLabel jLabel34;
     private javax.swing.JButton jButtonResetFields;

Modified: labs/jbosstm/trunk/XTS/demo/web/index.jsp
===================================================================
--- labs/jbosstm/trunk/XTS/demo/web/index.jsp	2010-09-24 14:01:49 UTC (rev 35271)
+++ labs/jbosstm/trunk/XTS/demo/web/index.jsp	2010-09-24 14:43:12 UTC (rev 35272)
@@ -165,7 +165,8 @@
 <div>
 <p>
 Book
-<SELECT NAME="theatrecount">
+<SELECT NAME="theatrecirclecount">
+<OPTION>0
 <OPTION>1
 <OPTION>2
 <OPTION>3
@@ -177,13 +178,42 @@
 <OPTION>9
 <OPTION>10
 </SELECT>
-seats in the
-<select name="theatrearea">
-<option value="0">Circle
-<option value="1">Stalls
-<option value="2">Balcony
-</select>
+seats in the Circle
 </p>
+<p>
+Book
+<SELECT NAME="theatrestallscount">
+<OPTION>0
+<OPTION>1
+<OPTION>2
+<OPTION>3
+<OPTION>4
+<OPTION>5
+<OPTION>6
+<OPTION>7
+<OPTION>8
+<OPTION>9
+<OPTION>10
+</SELECT>
+seats in the Stalls
+</p>
+<p>
+Book
+<SELECT NAME="theatrebalconycount">
+<OPTION>0
+<OPTION>1
+<OPTION>2
+<OPTION>3
+<OPTION>4
+<OPTION>5
+<OPTION>6
+<OPTION>7
+<OPTION>8
+<OPTION>9
+<OPTION>10
+</SELECT>
+seats in the Balcony
+</p>
 </FONT></TD>
 <TD width="10" bgcolor="#e3e3e3">&nbsp;</TD>
 </TR>



More information about the jboss-svn-commits mailing list