[jboss-svn-commits] JBL Code SVN: r21312 - in labs/jbosstm/trunk/XTS: demo/ddrpc/jboss and 5 other directories.
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Thu Jul 31 09:27:12 EDT 2008
Author: adinn
Date: 2008-07-31 09:27:11 -0400 (Thu, 31 Jul 2008)
New Revision: 21312
Added:
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCATRecoveryListener.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCATRecoveryModule.java
Modified:
labs/jbosstm/trunk/XTS/WS-T/dev/src10/org/jboss/jbossts/xts10/recovery/participant/at/ATParticipantRecoveryRecord.java
labs/jbosstm/trunk/XTS/demo/ddrpc/jboss/service-web-app.xml
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantManager.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantParticipantAT.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantServiceAT.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiManager.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiParticipantAT.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiServiceAT.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreManager.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreParticipantAT.java
labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreServiceAT.java
Log:
udpated JaxRPC-based demo to implement participant persistence in order to exercise 1.0 participant recovery which seems to work ok -- for JBTM-121
Modified: labs/jbosstm/trunk/XTS/WS-T/dev/src10/org/jboss/jbossts/xts10/recovery/participant/at/ATParticipantRecoveryRecord.java
===================================================================
--- labs/jbosstm/trunk/XTS/WS-T/dev/src10/org/jboss/jbossts/xts10/recovery/participant/at/ATParticipantRecoveryRecord.java 2008-07-31 12:30:46 UTC (rev 21311)
+++ labs/jbosstm/trunk/XTS/WS-T/dev/src10/org/jboss/jbossts/xts10/recovery/participant/at/ATParticipantRecoveryRecord.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -7,8 +7,10 @@
import com.arjuna.webservices.wsaddr.EndpointReferenceType;
import com.arjuna.webservices.wsat.State;
import com.arjuna.webservices.wsat.processors.ParticipantProcessor;
+import com.arjuna.webservices.util.StreamHelper;
import javax.xml.stream.*;
+import javax.xml.namespace.QName;
import java.io.StringWriter;
import java.io.IOException;
import java.io.StringReader;
@@ -49,7 +51,9 @@
XMLOutputFactory factory = XMLOutputFactory.newInstance();
StringWriter stringWriter = new StringWriter();
XMLStreamWriter writer = factory.createXMLStreamWriter(stringWriter);
+ StreamHelper.writeStartElement(writer, QNAME_TWO_PC_COORDINATOR) ;
endpoint.writeContent(writer);
+ StreamHelper.writeEndElement(writer, null, null) ;
writer.close();
oos.packString(stringWriter.toString());
}
@@ -63,6 +67,7 @@
StringReader stringReader = new StringReader(xmlEndpoint);
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(stringReader);
+ StreamHelper.checkNextStartTag(reader, QNAME_TWO_PC_COORDINATOR) ;
endpoint = new EndpointReferenceType(reader);
}
@@ -85,5 +90,7 @@
return ParticipantProcessor.getProcessor().isActive(getId());
}
+ private static final QName QNAME_TWO_PC_COORDINATOR = new QName("twoPCCoordinator") ;
+
private EndpointReferenceType endpoint;
}
Modified: labs/jbosstm/trunk/XTS/demo/ddrpc/jboss/service-web-app.xml
===================================================================
--- labs/jbosstm/trunk/XTS/demo/ddrpc/jboss/service-web-app.xml 2008-07-31 12:30:46 UTC (rev 21311)
+++ labs/jbosstm/trunk/XTS/demo/ddrpc/jboss/service-web-app.xml 2008-07-31 13:27:11 UTC (rev 21312)
@@ -24,6 +24,9 @@
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
+ <listener>
+ <listener-class>com.arjuna.xts.nightout.services.recovery.DemoRPCATRecoveryListener</listener-class>
+ </listener>
<servlet>
<servlet-name>RestaurantServiceAT</servlet-name>
<servlet-class>com.arjuna.xts.nightout.services.Restaurant.RestaurantServiceAT</servlet-class>
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantManager.java 2008-07-31 12:30:46 UTC (rev 21311)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantManager.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -30,6 +30,8 @@
package com.arjuna.xts.nightout.services.Restaurant;
import java.util.Hashtable;
+import java.util.Enumeration;
+import java.io.*;
/**
* The transactional application logic for the Restaurant Service.
@@ -37,17 +39,30 @@
* Stores and manages seating reservations. Knows nothing about Web Services.
* Understands transactional booking lifecycle: unprepared, prepared, finished.
*
+ * </p>The manager maintains the following invariants regarding seating capacity:
+ * <ul>
+ * <li>nBooked == sum(unpreparedList.seatCount) + sum(preparedList.seatCount)
+ *
+ * <li>nPrepared = sum(prepared.seatCount)
+ *
+ * <li>nTotal == nFree + nPrepared + nCommitted
+ * </ul>
+ * changes to nPrepared, nFree, nCommitted, nTotal and preparedList are always shadowed in
+ * persistent storage before returning control to clients.
+ *
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
*/
-public class RestaurantManager
+public class RestaurantManager implements Serializable
{
/**
* Create and initialise a new RestaurantManager instance.
*/
- public RestaurantManager()
+ private RestaurantManager()
{
- setToDefault();
+ setToDefault(false);
+ // restore any state saved by a previous installation of this web service
+ restoreState();
}
/**
@@ -56,7 +71,7 @@
* @param txID The transaction identifier
* @param nSeats The number of seats requested
*/
- public void bookSeats(Object txID, int nSeats)
+ public synchronized void bookSeats(Object txID, int nSeats)
{
// locate any pre-existing request for the same transaction
Integer request = (Integer) unpreparedTransactions.get(txID);
@@ -73,6 +88,7 @@
// record the increased commitment to provide seating
nBookedSeats += nSeats;
+ // we don't actually need to update until prepare
}
/**
@@ -81,7 +97,7 @@
* @param txID The transaction identifier
* @return true on success, false otherwise
*/
- public boolean prepareSeats(Object txID)
+ public synchronized boolean prepareSeats(Object txID)
{
// ensure that we have seen this transaction before
Integer request = (Integer) unpreparedTransactions.get(txID);
@@ -101,6 +117,8 @@
// mark the prepared seats as unavailable
nFreeSeats -= request.intValue();
nPreparedSeats += request.intValue();
+ updateState();
+
return true;
}
else
@@ -130,6 +148,7 @@
// mark the prepared seats as unavailable
nFreeSeats -= request.intValue();
nPreparedSeats += request.intValue();
+ updateState();
return true;
}
else
@@ -152,7 +171,7 @@
* @param txID The transaction identifier
* @return true on success, false otherwise
*/
- public boolean cancelSeats(Object txID)
+ public synchronized boolean cancelSeats(Object txID)
{
boolean success = false;
@@ -165,6 +184,7 @@
nFreeSeats += request.intValue();
nPreparedSeats -= request.intValue();
nBookedSeats -= request.intValue();
+ updateState();
success = true;
}
else if (unpreparedTransactions.containsKey(txID))
@@ -178,7 +198,6 @@
{
success = false; // error: transaction not registered
}
-
return success;
}
@@ -188,7 +207,7 @@
* @param txID The transaction identifier
* @return true on success, false otherwise
*/
- public boolean commitSeats(Object txID)
+ public synchronized boolean commitSeats(Object txID)
{
boolean success = false;
@@ -201,6 +220,7 @@
nCommittedSeats += request.intValue();
nPreparedSeats -= request.intValue();
nBookedSeats -= request.intValue();
+ updateState();
success = true;
}
else if (unpreparedTransactions.containsKey(txID))
@@ -210,6 +230,7 @@
nCommittedSeats += request.intValue();
nFreeSeats -= request.intValue();
nBookedSeats -= request.intValue();
+ updateState();
success = true;
}
else
@@ -354,10 +375,21 @@
}
/**
- * (re-)initialise the instance data structures.
+ * (re-)initialise the instance data structures deleting any previously saved
+ * transaction state.
*/
public void setToDefault()
{
+ setToDefault(true);
+ }
+
+ /**
+ * (re-)initialise the instance data structures, potentially committing any saved state
+ * to disk
+ * @param deleteSavedState true if any cached transaction state should be deleted otherwise false
+ */
+ public void setToDefault(boolean deleteSavedState)
+ {
nTotalSeats = DEFAULT_SEATING_CAPACITY;
nFreeSeats = nTotalSeats;
nBookedSeats = 0;
@@ -369,6 +401,10 @@
preparation = new Object();
isPreparationWaiting = false;
isCommit = true;
+ if (deleteSavedState) {
+ // just write the current state.
+ updateState();
+ }
}
/**
@@ -376,7 +412,7 @@
*
* @return the singleton RestaurantManager instance.
*/
- public static RestaurantManager getSingletonInstance()
+ public synchronized static RestaurantManager getSingletonInstance()
{
if (singletonInstance == null)
{
@@ -453,4 +489,126 @@
* The default initial capacity of each seating area.
*/
public static final int DEFAULT_SEATING_CAPACITY = 100;
+
+ /**
+ * the name of the file used to store the restaurant manager state
+ */
+ final static private String STATE_FILENAME = "restaurantManagerRPCState";
+
+ /**
+ * the name of the file used to store the restaurant manager shadow state
+ */
+ final static private String SHADOW_STATE_FILENAME = "restaurantManagerRPCShadowState";
+
+ /**
+ * load any previously saved manager state
+ *
+ * n.b. can only be called once from the singleton constructor before save can be called
+ * so there is no need for any synchronization here
+ */
+
+ private void restoreState()
+ {
+ File file = new File(STATE_FILENAME);
+ File shadowFile = new File(SHADOW_STATE_FILENAME);
+ if (file.exists()) {
+ if (shadowFile.exists()) {
+ // crashed during shadow file write == just trash it
+ shadowFile.delete();
+ }
+ } else if (shadowFile.exists()) {
+ // crashed afetr successful write - promote shadow file to real file
+ shadowFile.renameTo(file);
+ file = new File(STATE_FILENAME);
+ }
+ if (file.exists()) {
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ readState(ois);
+ } catch (Exception e) {
+ System.out.println("error : could not restore restaurant manager state" + e);
+ }
+ } else {
+ System.out.println("Starting with default restaurant manager state");
+ }
+ }
+
+ /**
+ * write the current manager state to a shadow disk file then commit it as the latest state
+ * by relinking it to the current file
+ *
+ * n.b. must always called synchronized since the caller must always atomically check the
+ * current state, modify it and write it.
+ */
+ private void updateState()
+ {
+ File file = new File(STATE_FILENAME);
+ File shadowFile = new File(SHADOW_STATE_FILENAME);
+
+ if (shadowFile.exists()) {
+ // previous write must have barfed
+ shadowFile.delete();
+ }
+
+ try {
+ FileOutputStream fos = new FileOutputStream(shadowFile);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ writeState(oos);
+ } catch (Exception e) {
+ System.out.println("error : could not restore restaurant manager state" + e);
+ }
+
+ shadowFile.renameTo(file);
+ }
+
+ /**
+ * does the actual work of reading in the saved manager state
+ *
+ * @param ois
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ private void readState(ObjectInputStream ois) throws IOException, ClassNotFoundException
+ {
+ nTotalSeats = ois.readInt();
+ nFreeSeats = ois.readInt();
+ nPreparedSeats = ois.readInt();
+ nCommittedSeats = ois.readInt();
+ preparedTransactions = new Hashtable();
+ String name = (String)ois.readObject();
+ while (!"".equals(name)) {
+ int count = ois.readInt();
+ preparedTransactions.put(name, new Integer(count));
+ name = (String)ois.readObject();
+ }
+ unpreparedTransactions = new Hashtable();
+ // derive nBookedSeats from invariant
+ nBookedSeats = nPreparedSeats;
+ // assert invariant for total seats
+ assert nTotalSeats == nFreeSeats + nPreparedSeats + nCommittedSeats;
+ }
+
+ /**
+ * does the actual work of writing out the saved manager state
+ * @param oos
+ * @throws IOException
+ */
+ private void writeState(ObjectOutputStream oos) throws IOException
+ {
+ // assert invariant for total seats
+ assert nTotalSeats == nFreeSeats + nPreparedSeats + nCommittedSeats;
+ oos.writeInt(nTotalSeats);
+ oos.writeInt(nFreeSeats);
+ oos.writeInt(nPreparedSeats);
+ oos.writeInt(nCommittedSeats);
+ Enumeration keys = preparedTransactions.keys();
+ while (keys.hasMoreElements()) {
+ String name = (String)keys.nextElement();
+ int count = ((Integer)preparedTransactions.get(name)).intValue();
+ oos.writeObject(name);
+ oos.writeInt(count);
+ }
+ oos.writeObject("");
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantParticipantAT.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantParticipantAT.java 2008-07-31 12:30:46 UTC (rev 21311)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantParticipantAT.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -31,6 +31,9 @@
import com.arjuna.wst.*;
+import java.io.Serializable;
+import java.io.IOException;
+
/**
* An adapter class that exposes the RestaurantManager transaction lifecycle
* API as a WS-T Atomic Transaction participant.
@@ -39,7 +42,7 @@
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
*/
-public class RestaurantParticipantAT implements Durable2PCParticipant
+public class RestaurantParticipantAT implements Durable2PCParticipant, Serializable
{
/**
* Participant instances are related to transaction instances
@@ -49,9 +52,6 @@
*/
public RestaurantParticipantAT(String txID)
{
- // Binds to the singleton RestaurantView and RestaurantManager
- restaurantManager = RestaurantManager.getSingletonInstance();
- restaurantView = RestaurantView.getSingletonInstance();
// we need to save the txID for later use when calling
// business logic methods in the restaurantManger.
this.txID = txID;
@@ -72,24 +72,24 @@
System.out.println("RestaurantParticipantAT.prepare");
- restaurantView.addPrepareMessage("id:" + txID + ". Prepare called on participant: " + this.getClass().toString());
+ getRestaurantView().addPrepareMessage("id:" + txID + ". Prepare called on participant: " + this.getClass().toString());
- boolean success = restaurantManager.prepareSeats(txID);
+ boolean success = getRestaurantManager().prepareSeats(txID);
// Log the outcome and map the return value from
// the business logic to the appropriate Vote type.
if (success)
{
- restaurantView.addMessage("Seats prepared successfully. Returning 'Prepared'\n");
- restaurantView.updateFields();
+ getRestaurantView().addMessage("Seats prepared successfully. Returning 'Prepared'\n");
+ getRestaurantView().updateFields();
return new Prepared();
}
else
{
- restaurantManager.cancelSeats(txID) ;
- restaurantView.addMessage("Prepare failed (not enough seats?) Returning 'Aborted'\n");
- restaurantView.updateFields();
+ getRestaurantManager().cancelSeats(txID) ;
+ getRestaurantView().addMessage("Prepare failed (not enough seats?) Returning 'Aborted'\n");
+ getRestaurantView().updateFields();
return new Aborted();
}
}
@@ -108,22 +108,22 @@
System.out.println("RestaurantParticipantAT.commit");
- restaurantView.addMessage("id:" + txID + ". Commit called on participant: " + this.getClass().toString());
+ getRestaurantView().addMessage("id:" + txID + ". Commit called on participant: " + this.getClass().toString());
- boolean success = restaurantManager.commitSeats(txID);
+ boolean success = getRestaurantManager().commitSeats(txID);
// Log the outcome
if (success)
{
- restaurantView.addMessage("Seats committed\n");
+ getRestaurantView().addMessage("Seats committed\n");
}
else
{
- restaurantView.addMessage("Something went wrong (Transaction not registered?)\n");
+ getRestaurantView().addMessage("Something went wrong (Transaction not registered?)\n");
}
- restaurantView.updateFields();
+ getRestaurantView().updateFields();
}
/**
@@ -140,22 +140,22 @@
System.out.println("RestaurantParticipantAT.rollback");
- restaurantView.addMessage("id:" + txID + ". Rollback called on participant: " + this.getClass().toString());
+ getRestaurantView().addMessage("id:" + txID + ". Rollback called on participant: " + this.getClass().toString());
- boolean success = restaurantManager.cancelSeats(txID);
+ boolean success = getRestaurantManager().cancelSeats(txID);
// Log the outcome
if (success)
{
- restaurantView.addMessage("Seats booking cancelled\n");
+ getRestaurantView().addMessage("Seats booking cancelled\n");
}
else
{
- restaurantView.addMessage("Something went wrong (Transaction not registered?)\n");
+ getRestaurantView().addMessage("Something went wrong (Transaction not registered?)\n");
}
- restaurantView.updateFields();
+ getRestaurantView().updateFields();
}
/**
@@ -188,14 +188,12 @@
*/
protected String txID;
- /**
- * The RestaurantView object to log events through.
- */
- protected static RestaurantView restaurantView;
+ public RestaurantView getRestaurantView() {
+ return RestaurantView.getSingletonInstance();
+ }
- /**
- * The RestaurantManager to perform business logic operations on.
- */
- protected static RestaurantManager restaurantManager;
+ public RestaurantManager getRestaurantManager() {
+ return RestaurantManager.getSingletonInstance();
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantServiceAT.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantServiceAT.java 2008-07-31 12:30:46 UTC (rev 21311)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Restaurant/RestaurantServiceAT.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -66,7 +66,7 @@
System.out.println("RestaurantServiceAT - enrolling...");
// enlist the Participant for this service:
RestaurantParticipantAT restaurantParticipant = new RestaurantParticipantAT(transactionId);
- TransactionManagerFactory.transactionManager().enlistForDurableTwoPhase(restaurantParticipant, new Uid().toString());
+ TransactionManagerFactory.transactionManager().enlistForDurableTwoPhase(restaurantParticipant, "org.jboss.jbossts.xts-demorpc:restaurantAT:" + new Uid().toString());
}
}
catch (Exception e)
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiManager.java 2008-07-31 12:30:46 UTC (rev 21311)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiManager.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -30,6 +30,8 @@
package com.arjuna.xts.nightout.services.Taxi;
import java.util.Hashtable;
+import java.util.Enumeration;
+import java.io.*;
/**
* The transactional application logic for the Taxi Service
@@ -37,17 +39,21 @@
* Stores and manages taxi reservations. Knows nothing about Web Services.
* Understands transactional booking lifecycle: unprepared, prepared, finished.
*
+ * </p>changes to preparedList are always shadowed in persistent storage before
+ * returning control to clients.
+ *
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
*/
-public class TaxiManager
+public class TaxiManager implements Serializable
{
/**
* Create and initialise a new TaxiManager instance.
*/
- public TaxiManager()
+ private TaxiManager()
{
- setToDefault();
+ setToDefault(false);
+ restoreState();
}
/**
@@ -55,7 +61,7 @@
*
* @param txID The transaction identifier
*/
- public void bookTaxi(Object txID)
+ public synchronized void bookTaxi(Object txID)
{
// locate any pre-existing request for the same transaction
Integer request = (Integer) unpreparedTransactions.get(txID);
@@ -68,6 +74,7 @@
// record the request, keyed to its transaction scope
unpreparedTransactions.put(txID, new Integer(request.intValue()));
+ // we don't actually need to update until prepare
}
/**
@@ -76,7 +83,7 @@
* @param txID The transaction identifier
* @return true on success, false otherwise
*/
- public boolean prepareTaxi(Object txID)
+ public synchronized boolean prepareTaxi(Object txID)
{
// ensure that we have seen this transaction before
Integer request = (Integer) unpreparedTransactions.get(txID);
@@ -91,6 +98,7 @@
// record the prepared transaction
preparedTransactions.put(txID, request);
unpreparedTransactions.remove(txID);
+ updateState();
return true;
}
else
@@ -109,6 +117,7 @@
// record the prepared transaction
preparedTransactions.put(txID, request);
unpreparedTransactions.remove(txID);
+ updateState();
return true;
}
else
@@ -132,7 +141,7 @@
* @param txID The transaction identifier
* @return true on success, false otherwise
*/
- public boolean cancelTaxi(Object txID)
+ public synchronized boolean cancelTaxi(Object txID)
{
boolean success = false;
@@ -142,12 +151,14 @@
{
// undo the prepare operations
preparedTransactions.remove(txID);
+ updateState();
success = true;
}
else if (unpreparedTransactions.containsKey(txID))
{
// undo the booking operations
unpreparedTransactions.remove(txID);
+ // we don't need to update state
success = true;
}
else
@@ -164,7 +175,7 @@
* @param txID The transaction identifier
* @return true on success, false otherwise
*/
- public boolean commitTaxi(Object txID)
+ public synchronized boolean commitTaxi(Object txID)
{
boolean success = false;
hasCommitted = true;
@@ -175,12 +186,14 @@
{
// complete the prepared transaction
preparedTransactions.remove(txID);
+ updateState();
success = true;
}
else if (unpreparedTransactions.containsKey(txID))
{
// use one phase commit optimisation, skipping prepare
unpreparedTransactions.remove(txID);
+ // we don't need to update state
success = true;
}
else
@@ -262,10 +275,21 @@
}
/**
- * (re-)initialise the instance data structures.
+ * (re-)initialise the instance data structures deleting any previously saved
+ * transaction state.
*/
public void setToDefault()
{
+ setToDefault(true);
+ }
+
+ /**
+ * (re-)initialise the instance data structures, potentially committing any saved state
+ * to disk
+ * @param deleteSavedState true if any cached transaction state should be deleted otherwise false
+ */
+ public void setToDefault(boolean deleteSavedState)
+ {
preparedTransactions = new Hashtable();
unpreparedTransactions = new Hashtable();
autoCommitMode = true;
@@ -273,12 +297,16 @@
isPreparationWaiting = false;
isCommit = false;
hasCommitted = false;
+ if (deleteSavedState) {
+ // just write the current state.
+ updateState();
+ }
}
/**
* Allow use of a singleton model for web services demo.
*/
- public static TaxiManager getSingletonInstance()
+ public synchronized static TaxiManager getSingletonInstance()
{
if (singletonInstance == null)
{
@@ -344,4 +372,112 @@
* The waiting status, when in manual commit mode.
*/
private boolean isPreparationWaiting;
+
+ /**
+ * the name of the file sued to store the restaurant manager state
+ */
+ final static private String STATE_FILENAME = "taxiManagerRPCState";
+
+ /**
+ * the name of the file sued to store the restaurant manager shadow state
+ */
+ final static private String SHADOW_STATE_FILENAME = "taxiManagerRPCShadowState";
+
+ /**
+ * load any previously saved manager state.
+ *
+ * n.b. can only be called once from the singleton constructor before save can be called
+ * so there is no need for any synchronization here
+ */
+
+ private synchronized void restoreState()
+ {
+ File file = new File(STATE_FILENAME);
+ File shadowFile = new File(SHADOW_STATE_FILENAME);
+ if (file.exists()) {
+ if (shadowFile.exists()) {
+ // crashed during shadow file write == just trash it
+ shadowFile.delete();
+ }
+ } else if (shadowFile.exists()) {
+ // crashed afetr successful write - promote shadow file to real file
+ shadowFile.renameTo(file);
+ file = new File(STATE_FILENAME);
+ }
+ if (file.exists()) {
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ readState(ois);
+ } catch (Exception e) {
+ System.out.println("error : could not restore restaurant manager state" + e);
+ }
+ } else {
+ System.out.println("Starting with default restaurant manager state");
+ }
+ }
+
+ /**
+ * write the current manager state to a shadow disk file then commit it as the latest state
+ * by relinking it to the current file
+ *
+ * n.b. must always called synchronized since the caller must always atomically check the
+ * current state, modify it and write it.
+ */
+ private void updateState()
+ {
+ File file = new File(STATE_FILENAME);
+ File shadowFile = new File(SHADOW_STATE_FILENAME);
+
+ if (shadowFile.exists()) {
+ // previous write must have barfed
+ shadowFile.delete();
+ }
+
+ try {
+ FileOutputStream fos = new FileOutputStream(shadowFile);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ writeState(oos);
+ } catch (Exception e) {
+ System.out.println("error : could not restore restaurant manager state" + e);
+ }
+
+ shadowFile.renameTo(file);
+ }
+
+ /**
+ * does the actual work of reading in the saved manager state
+ *
+ * @param ois
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ private void readState(ObjectInputStream ois) throws IOException, ClassNotFoundException
+ {
+ preparedTransactions = new Hashtable();
+ String name = (String)ois.readObject();
+ while (!"".equals(name)) {
+ int count = ois.readInt();
+ preparedTransactions.put(name, new Integer(count));
+ name = (String)ois.readObject();
+ }
+ unpreparedTransactions = new Hashtable();
+ }
+
+ /**
+ * does the actual work of writing out the saved manager state
+ * @param oos
+ * @throws IOException
+ */
+ private void writeState(ObjectOutputStream oos) throws IOException
+ {
+ Enumeration keys = preparedTransactions.keys();
+ while (keys.hasMoreElements()) {
+ String name = (String)keys.nextElement();
+ int count = ((Integer)preparedTransactions.get(name)).intValue();
+ oos.writeObject(name);
+ oos.writeInt(count);
+ }
+ oos.writeObject("");
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiParticipantAT.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiParticipantAT.java 2008-07-31 12:30:46 UTC (rev 21311)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiParticipantAT.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -31,6 +31,8 @@
import com.arjuna.wst.*;
+import java.io.Serializable;
+
/**
* An adapter class that exposes the TaxiManager transaction lifecycle
* API as a WS-T Atomic Transaction participant.
@@ -39,7 +41,7 @@
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
*/
-public class TaxiParticipantAT implements Durable2PCParticipant
+public class TaxiParticipantAT implements Durable2PCParticipant, Serializable
{
/**
* Participant instances are related to transaction instances
@@ -49,9 +51,6 @@
*/
public TaxiParticipantAT(String txID)
{
- // Binds to the singleton TaxiView and TaxiManager
- taxiManager = TaxiManager.getSingletonInstance();
- taxiView = TaxiView.getSingletonInstance();
// we need to save the txID for later use when calling
// business logic methods in the taxiManger.
this.txID = txID;
@@ -72,24 +71,24 @@
System.out.println("TaxiParticipantAT.prepare");
- taxiView.addPrepareMessage("id:" + txID + ". Prepare called on participant: " + this.getClass().toString());
+ getTaxiView().addPrepareMessage("id:" + txID + ". Prepare called on participant: " + this.getClass().toString());
- boolean success = taxiManager.prepareTaxi(txID);
+ boolean success = getTaxiManager().prepareTaxi(txID);
// Log the outcome and map the return value from
// the business logic to the appropriate Vote type.
if (success)
{
- taxiView.addMessage("Taxi prepared successfully. Returning 'Prepared'\n");
- taxiView.updateFields();
+ getTaxiView().addMessage("Taxi prepared successfully. Returning 'Prepared'\n");
+ getTaxiView().updateFields();
return new Prepared();
}
else
{
- taxiManager.cancelTaxi(txID) ;
- taxiView.addMessage("Prepare failed (not enough Taxis?) Returning 'Aborted'\n");
- taxiView.updateFields();
+ getTaxiManager().cancelTaxi(txID) ;
+ getTaxiView().addMessage("Prepare failed (not enough Taxis?) Returning 'Aborted'\n");
+ getTaxiView().updateFields();
return new Aborted();
}
}
@@ -108,22 +107,22 @@
System.out.println("TaxiParticipantAT.commit");
- taxiView.addMessage("id:" + txID + ". Commit called on participant: " + this.getClass().toString());
+ getTaxiView().addMessage("id:" + txID + ". Commit called on participant: " + this.getClass().toString());
- boolean success = taxiManager.commitTaxi(txID);
+ boolean success = getTaxiManager().commitTaxi(txID);
// Log the outcome
if (success)
{
- taxiView.addMessage("Taxi committed\n");
+ getTaxiView().addMessage("Taxi committed\n");
}
else
{
- taxiView.addMessage("Something went wrong (Transaction not registered?)\n");
+ getTaxiView().addMessage("Something went wrong (Transaction not registered?)\n");
}
- taxiView.updateFields();
+ getTaxiView().updateFields();
}
/**
@@ -140,22 +139,22 @@
System.out.println("TaxiParticipantAT.rollback");
- taxiView.addMessage("id:" + txID + ". Rollback called on participant: " + this.getClass().toString());
+ getTaxiView().addMessage("id:" + txID + ". Rollback called on participant: " + this.getClass().toString());
- boolean success = taxiManager.cancelTaxi(txID);
+ boolean success = getTaxiManager().cancelTaxi(txID);
// Log the outcome
if (success)
{
- taxiView.addMessage("Taxi booking cancelled\n");
+ getTaxiView().addMessage("Taxi booking cancelled\n");
}
else
{
- taxiView.addMessage("Something went wrong (Transaction not registered?)\n");
+ getTaxiView().addMessage("Something went wrong (Transaction not registered?)\n");
}
- taxiView.updateFields();
+ getTaxiView().updateFields();
}
/**
@@ -188,13 +187,11 @@
*/
protected String txID;
- /**
- * The TaxiView object to log events through.
- */
- protected static TaxiView taxiView;
+ public TaxiView getTaxiView() {
+ return TaxiView.getSingletonInstance();
+ }
- /**
- * The TaxiManager to perform business logic operations on.
- */
- protected static TaxiManager taxiManager;
+ public TaxiManager getTaxiManager() {
+ return TaxiManager.getSingletonInstance();
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiServiceAT.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiServiceAT.java 2008-07-31 12:30:46 UTC (rev 21311)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Taxi/TaxiServiceAT.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -64,7 +64,7 @@
System.out.println("TaxiServiceAT - enrolling...");
// enlist the Participant for this service:
TaxiParticipantAT taxiParticipant = new TaxiParticipantAT(transactionId);
- TransactionManagerFactory.transactionManager().enlistForDurableTwoPhase(taxiParticipant, new Uid().toString());
+ TransactionManagerFactory.transactionManager().enlistForDurableTwoPhase(taxiParticipant, "org.jboss.jbossts.xts-demorpc:taxiAT:" + new Uid().toString());
}
}
catch (Exception e)
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreManager.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreManager.java 2008-07-31 12:30:46 UTC (rev 21311)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreManager.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -30,6 +30,8 @@
package com.arjuna.xts.nightout.services.Theatre;
import java.util.Hashtable;
+import java.util.Enumeration;
+import java.io.*;
/**
* The transactional application logic for the Theatre Service.
@@ -37,17 +39,29 @@
* Stores and manages seating reservations. Knows nothing about Web Services.
* Understands transactional booking lifecycle: unprepared, prepared, finished.
*
+ * </p>The manager maintains the following invariants regarding seating capacity:
+ * <ul>
+ * <li>nBooked[area] == sum(unpreparedList.seatCount[area]) + sum(preparedList.seatCount[area])
+ *
+ * <li>nPrepared[area] = sum(prepared.seatCount[area])
+ *
+ * <li>nTotal[area] == nFree[area] + nPrepared[area] + nCommitted[area]
+ * </ul>
+ * changes to nPrepared, nFree, nCommitted, nTotal and preparedList are always shadowed in
+ * persistent storage before returning control to clients.
+ *
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.4 $
*/
-public class TheatreManager
+public class TheatreManager implements Serializable
{
/**
* Create and initialise a new TheatreManager instance.
*/
public TheatreManager()
{
- setToDefault();
+ setToDefault(false);
+ restoreState();
}
/**
@@ -57,7 +71,7 @@
* @param nSeats The number of seats requested
* @param area The type of seating requested
*/
- public void bookSeats(Object txID, int nSeats, int area)
+ public synchronized void bookSeats(Object txID, int nSeats, int area)
{
// locate any pre-existing request for the same transaction
Integer[] requests = (Integer[]) unpreparedTransactions.get(txID);
@@ -78,6 +92,7 @@
// record the increased commitment to provide seating
nBookedSeats[area] += nSeats;
+ // we don't actually need to update until prepare
}
/**
@@ -86,7 +101,7 @@
* @param txID The transaction identifier
* @return true on success, false otherwise
*/
- public boolean prepareSeats(Object txID)
+ public synchronized boolean prepareSeats(Object txID)
{
int[] nSeats = new int[NUM_SEAT_AREAS];
@@ -126,6 +141,7 @@
nFreeSeats[i] = nSeats[i];
nPreparedSeats[i] += requests[i].intValue();
}
+ updateState();
}
return success;
}
@@ -153,6 +169,7 @@
nFreeSeats[i] = nSeats[i];
nPreparedSeats[i] += requests[i].intValue();
}
+ updateState();
return true;
}
else
@@ -191,6 +208,7 @@
nPreparedSeats[i] -= requests[i].intValue();
nBookedSeats[i] -= requests[i].intValue();
}
+ updateState();
success = true;
}
else if (unpreparedTransactions.containsKey(txID))
@@ -201,6 +219,7 @@
{
nBookedSeats[i] -= requests[i].intValue();
}
+ // we don't need to update state
success = true;
}
else
@@ -234,6 +253,7 @@
nPreparedSeats[i] -= requests[i].intValue();
nBookedSeats[i] -= requests[i].intValue();
}
+ updateState();
success = true;
}
else if (unpreparedTransactions.containsKey(txID))
@@ -246,6 +266,7 @@
nFreeSeats[i] -= requests[i].intValue();
nBookedSeats[i] -= requests[i].intValue();
}
+ updateState();
success = true;
}
else
@@ -394,10 +415,20 @@
}
/**
- * (re-)initialise the instance data structures.
+ * (re-)initialise the instance data structures deleting any previously saved
+ * transaction state.
*/
public void setToDefault()
{
+ setToDefault(true);
+ }
+ /**
+ * (re-)initialise the instance data structures, potentially committing any saved state
+ * to disk
+ * @param deleteSavedState true if any cached transaction state should be deleted otherwise false
+ */
+ public void setToDefault(boolean deleteSavedState)
+ {
nTotalSeats = new int[NUM_SEAT_AREAS];
nFreeSeats = new int[NUM_SEAT_AREAS];
nBookedSeats = new int[NUM_SEAT_AREAS];
@@ -417,12 +448,16 @@
preparation = new Object();
isPreparationWaiting = false;
isCommit = true;
+ if (deleteSavedState) {
+ // just write the current state.
+ updateState();
+ }
}
/**
* Allow use of a singleton model for web services demo.
*/
- public static TheatreManager getSingletonInstance()
+ public synchronized static TheatreManager getSingletonInstance()
{
if (singletonInstance == null)
{
@@ -526,4 +561,138 @@
* The default initial capacity of each seating area.
*/
public static final int DEFAULT_SEATING_CAPACITY = 100;
+
+ /**
+ * the name of the file sued to store the restaurant manager state
+ */
+ final static private String STATE_FILENAME = "theatreManagerRPCState";
+
+ /**
+ * the name of the file sued to store the restaurant manager shadow state
+ */
+ final static private String SHADOW_STATE_FILENAME = "theatreManagerRPCShadowState";
+
+ /**
+ * load any previously saved manager state
+ *
+ * n.b. can only be called once from the singleton constructor before save can be called
+ * so there is no need for any synchronization here
+ */
+
+ private void restoreState()
+ {
+ File file = new File(STATE_FILENAME);
+ File shadowFile = new File(SHADOW_STATE_FILENAME);
+ if (file.exists()) {
+ if (shadowFile.exists()) {
+ // crashed during shadow file write == just trash it
+ shadowFile.delete();
+ }
+ } else if (shadowFile.exists()) {
+ // crashed afetr successful write - promote shadow file to real file
+ shadowFile.renameTo(file);
+ file = new File(STATE_FILENAME);
+ }
+ if (file.exists()) {
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ readState(ois);
+ } catch (Exception e) {
+ System.out.println("error : could not restore restaurant manager state" + e);
+ }
+ } else {
+ System.out.println("Starting with default restaurant manager state");
+ }
+ }
+
+ /**
+ * write the current manager state to a shadow disk file then commit it as the latest state
+ * by relinking it to the current file
+ *
+ * n.b. must always called synchronized since the caller must always atomically check the
+ * current state, modify it and write it.
+ */
+ private void updateState()
+ {
+ File file = new File(STATE_FILENAME);
+ File shadowFile = new File(SHADOW_STATE_FILENAME);
+
+ if (shadowFile.exists()) {
+ // previous write must have barfed
+ shadowFile.delete();
+ }
+
+ try {
+ FileOutputStream fos = new FileOutputStream(shadowFile);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ writeState(oos);
+ } catch (Exception e) {
+ System.out.println("error : could not restore restaurant manager state" + e);
+ }
+
+ shadowFile.renameTo(file);
+ }
+
+ /**
+ * does the actual work of reading in the saved manager state
+ *
+ * @param ois
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ private void readState(ObjectInputStream ois) throws IOException, ClassNotFoundException
+ {
+ for (int i = 0; i < NUM_SEAT_AREAS; i++) {
+ nTotalSeats[i] = ois.readInt();
+ nFreeSeats[i] = ois.readInt();
+ nPreparedSeats[i] = ois.readInt();
+ nCommittedSeats[i] = ois.readInt();
+ }
+ preparedTransactions = new Hashtable();
+ String name = (String)ois.readObject();
+ while (!"".equals(name)) {
+ Integer[] counts = new Integer[NUM_SEAT_AREAS];
+ for (int i = 0; i < NUM_SEAT_AREAS; i++) {
+ int count = ois.readInt();
+ counts[i] = new Integer(count);
+ }
+ preparedTransactions.put(name, counts);
+ name = (String)ois.readObject();
+ }
+ unpreparedTransactions = new Hashtable();
+ for (int i = 0; i < NUM_SEAT_AREAS; i++) {
+ // derive nBookedSeats from invariant
+ nBookedSeats[i] = nPreparedSeats[i];
+ // assert invariant for total seats
+ assert nTotalSeats[i] == nFreeSeats[i] + nPreparedSeats[i] + nCommittedSeats[i];
+ }
+ }
+
+ /**
+ * does the actual work of writing out the saved manager state
+ * @param oos
+ * @throws IOException
+ */
+ private void writeState(ObjectOutputStream oos) throws IOException
+ {
+ for (int i = 0; i < NUM_SEAT_AREAS; i++) {
+ // assert invariant for total seats
+ assert nTotalSeats[i] == nFreeSeats[i] + nPreparedSeats[i] + nCommittedSeats[i];
+ oos.writeInt(nTotalSeats[i]);
+ oos.writeInt(nFreeSeats[i]);
+ oos.writeInt(nPreparedSeats[i]);
+ oos.writeInt(nCommittedSeats[i]);
+ }
+ Enumeration keys = preparedTransactions.keys();
+ while (keys.hasMoreElements()) {
+ String name = (String)keys.nextElement();
+ Integer[] counts = (Integer[]) preparedTransactions.get(name);
+ oos.writeObject(name);
+ for (int i = 0; i < NUM_SEAT_AREAS; i++) {
+ oos.writeInt(counts[i].intValue());
+ }
+ }
+ oos.writeObject("");
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreParticipantAT.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreParticipantAT.java 2008-07-31 12:30:46 UTC (rev 21311)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreParticipantAT.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -31,6 +31,8 @@
import com.arjuna.wst.*;
+import java.io.Serializable;
+
/**
* An adapter class that exposes the TheatreManager transaction lifecycle
* API as a WS-T Atomic Transaction participant.
@@ -39,7 +41,7 @@
* @author Jonathan Halliday (jonathan.halliday at arjuna.com)
* @version $Revision: 1.3 $
*/
-public class TheatreParticipantAT implements Durable2PCParticipant
+public class TheatreParticipantAT implements Durable2PCParticipant, Serializable
{
/**
* Participant instances are related to transaction instances
@@ -49,9 +51,6 @@
*/
public TheatreParticipantAT(String txID)
{
- // Binds to the singleton TheatreView and TheatreManager
- theatreManager = TheatreManager.getSingletonInstance();
- theatreView = TheatreView.getSingletonInstance();
// we need to save the txID for later use when calling
// business logic methods in the theatreManger.
this.txID = txID;
@@ -72,9 +71,9 @@
System.out.println("TheatreParticipantAT.prepare");
- theatreView.addPrepareMessage("id:" + txID + ". Prepare called on participant: " + this.getClass().toString());
+ getTheatreView().addPrepareMessage("id:" + txID + ". Prepare called on participant: " + this.getClass().toString());
- boolean success = theatreManager.prepareSeats(txID);
+ boolean success = getTheatreManager().prepareSeats(txID);
// Log the outcome and map the return value from
// the business logic to the appropriate Vote type.
@@ -82,15 +81,15 @@
if (success)
{
- theatreView.addMessage("Theatre prepared successfully. Returning 'Prepared'\n");
- theatreView.updateFields();
+ getTheatreView().addMessage("Theatre prepared successfully. Returning 'Prepared'\n");
+ getTheatreView().updateFields();
return new Prepared();
}
else
{
- theatreManager.cancelSeats(txID) ;
- theatreView.addMessage("Prepare failed (not enough seats?) Returning 'Aborted'\n");
- theatreView.updateFields();
+ getTheatreManager().cancelSeats(txID) ;
+ getTheatreView().addMessage("Prepare failed (not enough seats?) Returning 'Aborted'\n");
+ getTheatreView().updateFields();
return new Aborted();
}
}
@@ -109,22 +108,22 @@
System.out.println("TheatreParticipantAT.commit");
- theatreView.addMessage("id:" + txID + ". Commit called on participant: " + this.getClass().toString());
+ getTheatreView().addMessage("id:" + txID + ". Commit called on participant: " + this.getClass().toString());
- boolean success = theatreManager.commitSeats(txID);
+ boolean success = getTheatreManager().commitSeats(txID);
// Log the outcome
if (success)
{
- theatreView.addMessage("Theatre tickets committed\n");
+ getTheatreView().addMessage("Theatre tickets committed\n");
}
else
{
- theatreView.addMessage("Something went wrong (Transaction not registered?)\n");
+ getTheatreView().addMessage("Something went wrong (Transaction not registered?)\n");
}
- theatreView.updateFields();
+ getTheatreView().updateFields();
}
/**
@@ -141,22 +140,22 @@
System.out.println("TheatreParticipantAT.rollback");
- theatreView.addMessage("id:" + txID + ". Rollback called on participant: " + this.getClass().toString());
+ getTheatreView().addMessage("id:" + txID + ". Rollback called on participant: " + this.getClass().toString());
- boolean success = theatreManager.cancelSeats(txID);
+ boolean success = getTheatreManager().cancelSeats(txID);
// Log the outcome
if (success)
{
- theatreView.addMessage("Theatre booking cancelled\n");
+ getTheatreView().addMessage("Theatre booking cancelled\n");
}
else
{
- theatreView.addMessage("Something went wrong (Transaction not registered?)\n");
+ getTheatreView().addMessage("Something went wrong (Transaction not registered?)\n");
}
- theatreView.updateFields();
+ getTheatreView().updateFields();
}
/**
@@ -189,13 +188,11 @@
*/
protected String txID;
- /**
- * The TheatreView object to log events through.
- */
- protected static TheatreView theatreView;
+ public TheatreView getTheatreView() {
+ return TheatreView.getSingletonInstance();
+ }
- /**
- * The TheatreManager to perform business logic operations on.
- */
- protected static TheatreManager theatreManager;
+ public TheatreManager getTheatreManager() {
+ return TheatreManager.getSingletonInstance();
+ }
}
Modified: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreServiceAT.java
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreServiceAT.java 2008-07-31 12:30:46 UTC (rev 21311)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/Theatre/TheatreServiceAT.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -66,7 +66,7 @@
System.out.println("theatreService - enrolling...");
// enlist the Participant for this service:
TheatreParticipantAT theatreParticipant = new TheatreParticipantAT(transactionId);
- TransactionManagerFactory.transactionManager().enlistForDurableTwoPhase(theatreParticipant, new Uid().toString());
+ TransactionManagerFactory.transactionManager().enlistForDurableTwoPhase(theatreParticipant, "org.jboss.jbossts.xts-demorpc:theatreAT:" + new Uid().toString());
}
}
catch (Exception e)
Copied: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCATRecoveryListener.java (from rev 21296, labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoATRecoveryListener.java)
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCATRecoveryListener.java (rev 0)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCATRecoveryListener.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -0,0 +1,21 @@
+package com.arjuna.xts.nightout.services.recovery;
+
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletContextEvent;
+
+/**
+ * Listener to register and unregister teh XTS application specific listener -- we have to
+ * use this because JBossWS does not currently honour the @PostConstruct and @PreDestroy
+ * lifecycle annotations on web services
+ */
+public class DemoRPCATRecoveryListener implements ServletContextListener
+{
+
+ public void contextInitialized(ServletContextEvent event) {
+ DemoRPCATRecoveryModule.register();
+ }
+
+ public void contextDestroyed(ServletContextEvent event) {
+ DemoRPCATRecoveryModule.unregister();
+ }
+}
\ No newline at end of file
Copied: labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCATRecoveryModule.java (from rev 21296, labs/jbosstm/trunk/XTS/demo/src/com/jboss/jbosstm/xts/demo/services/recovery/DemoATRecoveryModule.java)
===================================================================
--- labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCATRecoveryModule.java (rev 0)
+++ labs/jbosstm/trunk/XTS/demo/srcrpc/com/arjuna/xts/nightout/services/recovery/DemoRPCATRecoveryModule.java 2008-07-31 13:27:11 UTC (rev 21312)
@@ -0,0 +1,103 @@
+package com.arjuna.xts.nightout.services.recovery;
+
+import org.jboss.jbossts.xts.recovery.participant.at.XTSATRecoveryModule;
+import org.jboss.jbossts.xts.recovery.participant.at.XTSATRecoveryManager;
+import com.arjuna.wst.Durable2PCParticipant;
+
+import java.io.ObjectInputStream;
+
+/**
+ * Application-specific WS-AT participant recovery manager for demo application, This class
+ * is responsible for recreating application-specific durable participants from records
+ * logged at prepare time.
+ */
+public class DemoRPCATRecoveryModule implements XTSATRecoveryModule
+{
+ /**
+ * the singleton recovery module
+ */
+ private static DemoRPCATRecoveryModule theRecoveryModule = null;
+
+ /**
+ * a count of how many xts demo services are currently installed
+ */
+ private static int serviceCount = 0;
+
+ /**
+ * called during deployment of an xts-demo web service to ensure the recovery module for the
+ * demo is installed whenever any of the services is active
+ */
+ public static void register()
+ {
+ if (theRecoveryModule == null) {
+ theRecoveryModule = new DemoRPCATRecoveryModule();
+ }
+ if (serviceCount == 0) {
+ XTSATRecoveryManager.getRecoveryManager().registerRecoveryModule(theRecoveryModule);
+ }
+ serviceCount++;
+ }
+
+ /**
+ * called during undeployment of an xts-demo web service to ensure the recovery module for
+ * the demo is deinstalled once none of the services is active
+ */
+ public static void unregister()
+ {
+ if (serviceCount > 0) {
+ serviceCount--;
+ if (serviceCount == 0) {
+ XTSATRecoveryManager.getRecoveryManager().unregisterRecoveryModule(theRecoveryModule);
+ }
+ }
+ }
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and recreate the participant by deserializing
+ * it from the supplied object input stream. n.b. this is only appropriate in case the
+ * participant was originally saved using serialization.
+ *
+ * @param id the id used when the participant was created
+ * @param stream a stream from which the application should deserialise the participant
+ * if it recognises that the id belongs to the module's application
+ * @return
+ * @throws Exception if an error occurs deserializing the durable participant
+ */
+ public Durable2PCParticipant deserialize(String id, ObjectInputStream stream) throws Exception {
+ if (id.startsWith("org.jboss.jbossts.xts-demorpc:restaurantAT") ||
+ id.startsWith("org.jboss.jbossts.xts-demorpc:theatreAT") ||
+ id.startsWith("org.jboss.jbossts.xts-demorpc:taxiAT")) {
+ System.out.println("xts-demorpc : attempting to deserialize WS-AT participant " + id);
+ Durable2PCParticipant participant = (Durable2PCParticipant)stream.readObject();
+ System.out.println("xts-demorpc : deserialized WS-AT participant " + id);
+ return participant;
+ }
+
+ return null;
+ }
+
+ /**
+ * called during recovery processing to allow an application to identify a participant id
+ * belonging to one of its participants and use the saved recovery state to recreate the
+ * participant. n.b. this is only appropriate in case the participant was originally saved
+ * after being converted to a byte array using the PersistibleATParticipant interface.
+ *
+ * @param id the id used when the participant was created
+ * @param recoveryState a byte array returned form the original participant via a call to
+ * method getRecoveryState of interface PersistableATParticipant
+ * @return
+ * @throws Exception if an error occurs converting the recoveryState back to a
+ * durable participant
+ */
+ public Durable2PCParticipant recreate(String id, byte[] recoveryState) throws Exception {
+ if (id.startsWith("org.jboss.jbossts.xts-demorpc:restauarantAT") ||
+ id.startsWith("org.jboss.jbossts.xts-demorpc:theatreAT") ||
+ id.startsWith("org.jboss.jbossts.xts-demorpc:taxiAT")) {
+ // this should not get called -- xts-demo WS-AT participants are saved and restored
+ // using serialization
+ throw new Exception("xts-demorpc : invalid request to recreate() WS-AT participant " + id);
+ }
+ return null;
+ }
+}
\ No newline at end of file
More information about the jboss-svn-commits
mailing list