[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"> </TD>
</TR>
More information about the jboss-svn-commits
mailing list