Java/Design Pattern/Transaction Pattern
Transaction Pattern Demo
//[C] 2002 Sun Microsystems, Inc.---
import java.io.IOException;
import java.io.Serializable;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
public class RunTransactionPattern {
private static Calendar dateCreator = Calendar.getInstance();
public static void main(String[] arguments) {
System.out.println("Example for the Transaction pattern");
System.out.println("This code example shows how a Transaction can");
System.out
.println(" be applied to support change across a distributed");
System.out.println(" system. In ths case, a distributed transaction");
System.out.println(" is used to coordinate the change of dates in");
System.out.println(" appointment books.");
System.out.println("Running the RMI compiler (rmic)");
System.out.println();
try {
Process p1 = Runtime.getRuntime().exec("rmic AppointmentBook");
p1.waitFor();
} catch (IOException exc) {
System.err
.println("Unable to run rmic utility. Exiting application.");
System.exit(1);
} catch (InterruptedException exc) {
System.err
.println("Threading problems encountered while using the rmic utility.");
}
System.out.println("Starting the rmiregistry");
System.out.println();
try {
Process rmiProcess = Runtime.getRuntime().exec("rmiregistry");
Thread.sleep(15000);
} catch (IOException exc) {
System.err
.println("Unable to start the rmiregistry. Exiting application.");
System.exit(1);
} catch (InterruptedException exc) {
System.err
.println("Threading problems encountered when starting the rmiregistry.");
}
System.out.println("Creating three appointment books");
System.out.println();
AppointmentBook apptBookOne = new AppointmentBook();
AppointmentBook apptBookTwo = new AppointmentBook();
AppointmentBook apptBookThree = new AppointmentBook();
System.out.println("Creating appointments");
System.out.println();
Appointment apptOne = new AppointmentImpl(
"Swim relay to Kalimantan (or Java)", new ArrayList(),
new LocationImpl("Sidney, Australia"), createDate(2001, 11, 5,
11, 0));
Appointment apptTwo = new AppointmentImpl(
"Conference on World Patternization", new ArrayList(),
new LocationImpl("London, England"), createDate(2001, 11, 5,
14, 0));
Appointment apptThree = new AppointmentImpl(
"Society for the Preservation of Java - Annual Outing",
new ArrayList(), new LocationImpl("Kyzyl, Tuva"), createDate(
2001, 11, 5, 10, 0));
System.out.println("Adding appointments to the appointment books");
System.out.println();
apptBookOne.addAppointment(apptThree);
apptBookTwo.addAppointment(apptOne);
apptBookOne.addAppointment(apptTwo);
apptBookTwo.addAppointment(apptTwo);
apptBookThree.addAppointment(apptTwo);
System.out.println("AppointmentBook contents:");
System.out.println();
System.out.println(apptBookOne);
System.out.println(apptBookTwo);
System.out.println(apptBookThree);
System.out.println();
System.out.println("Rescheduling an appointment");
System.out.println();
System.out.println();
boolean result = apptBookThree.changeAppointment(apptTwo, getDates(
2001, 11, 5, 10, 3), lookUpParticipants(new String[] {
apptBookOne.getUrl(), apptBookTwo.getUrl(),
apptBookThree.getUrl() }), 20000L);
System.out.println("Result of rescheduling was " + result);
System.out.println("AppointmentBook contents:");
System.out.println();
System.out.println(apptBookOne);
System.out.println(apptBookTwo);
System.out.println(apptBookThree);
}
private static AppointmentTransactionParticipant[] lookUpParticipants(
String[] remoteUrls) {
AppointmentTransactionParticipant[] returnValues = new AppointmentTransactionParticipant[remoteUrls.length];
for (int i = 0; i < remoteUrls.length; i++) {
try {
returnValues[i] = (AppointmentTransactionParticipant) Naming
.lookup(remoteUrls[i]);
} catch (Exception exc) {
System.out
.println("Error using RMI to look up a transaction participant");
}
}
return returnValues;
}
private static Date[] getDates(int year, int month, int day, int hour,
int increment) {
Date[] returnDates = new Date[increment];
for (int i = 0; i < increment; i++) {
returnDates[i] = createDate(year, month, day, hour + i, 0);
}
return returnDates;
}
public static Date createDate(int year, int month, int day, int hour,
int minute) {
dateCreator.set(year, month, day, hour, minute);
return dateCreator.getTime();
}
}
interface Location extends Serializable {
public String getLocation();
public void setLocation(String newLocation);
}
class LocationImpl implements Location {
private String location;
public LocationImpl() {
}
public LocationImpl(String newLocation) {
location = newLocation;
}
public String getLocation() {
return location;
}
public void setLocation(String newLocation) {
location = newLocation;
}
public String toString() {
return location;
}
}
interface Contact extends Serializable {
public static final String SPACE = " ";
public String getFirstName();
public String getLastName();
public String getTitle();
public String getOrganization();
public void setFirstName(String newFirstName);
public void setLastName(String newLastName);
public void setTitle(String newTitle);
public void setOrganization(String newOrganization);
}
class ContactImpl implements Contact {
private String firstName;
private String lastName;
private String title;
private String organization;
public ContactImpl() {
}
public ContactImpl(String newFirstName, String newLastName,
String newTitle, String newOrganization) {
firstName = newFirstName;
lastName = newLastName;
title = newTitle;
organization = newOrganization;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getTitle() {
return title;
}
public String getOrganization() {
return organization;
}
public void setFirstName(String newFirstName) {
firstName = newFirstName;
}
public void setLastName(String newLastName) {
lastName = newLastName;
}
public void setTitle(String newTitle) {
title = newTitle;
}
public void setOrganization(String newOrganization) {
organization = newOrganization;
}
public String toString() {
return firstName + SPACE + lastName;
}
}
interface Appointment extends Serializable {
public static final String EOL_STRING = System
.getProperty("line.separator");
public Date getStartDate();
public String getDescription();
public ArrayList getAttendees();
public Location getLocation();
public void setDescription(String newDescription);
public void setLocation(Location newLocation);
public void setStartDate(Date newStartDate);
public void setAttendees(ArrayList newAttendees);
public void addAttendee(Contact attendee);
public void removeAttendee(Contact attendee);
}
class AppointmentImpl implements Appointment {
private Date startDate;
private String description;
private ArrayList attendees = new ArrayList();
private Location location;
public AppointmentImpl(String newDescription, ArrayList newAttendees,
Location newLocation, Date newStartDate) {
description = newDescription;
attendees = newAttendees;
location = newLocation;
startDate = newStartDate;
}
public Date getStartDate() {
return startDate;
}
public String getDescription() {
return description;
}
public ArrayList getAttendees() {
return attendees;
}
public Location getLocation() {
return location;
}
public void setDescription(String newDescription) {
description = newDescription;
}
public void setLocation(Location newLocation) {
location = newLocation;
}
public void setStartDate(Date newStartDate) {
startDate = newStartDate;
}
public void setAttendees(ArrayList newAttendees) {
if (newAttendees != null) {
attendees = newAttendees;
}
}
public void addAttendee(Contact attendee) {
if (!attendees.contains(attendee)) {
attendees.add(attendee);
}
}
public void removeAttendee(Contact attendee) {
attendees.remove(attendee);
}
public int hashCode() {
return description.hashCode() ^ startDate.hashCode();
}
public boolean equals(Object object) {
if (!(object instanceof AppointmentImpl)) {
return false;
}
if (object.hashCode() != hashCode()) {
return false;
}
return true;
}
public String toString() {
return " Description: " + description + EOL_STRING + " Start Date: "
+ startDate + EOL_STRING + " Location: " + location
+ EOL_STRING + " Attendees: " + attendees;
}
}
class AppointmentBook implements AppointmentTransactionParticipant {
private static final String TRANSACTION_SERVICE_PREFIX = "transactionParticipant";
private static final String TRANSACTION_HOSTNAME = "localhost";
private static int index = 1;
private String serviceName = TRANSACTION_SERVICE_PREFIX + index++;
private HashMap appointments = new HashMap();
private long currentTransaction;
private Appointment currentAppointment;
private Date updateStartDate;
public AppointmentBook() {
try {
UnicastRemoteObject.exportObject(this);
Naming.rebind(serviceName, this);
} catch (Exception exc) {
System.err
.println("Error using RMI to register the AppointmentBook "
+ exc);
}
}
public String getUrl() {
return "//" + TRANSACTION_HOSTNAME + "/" + serviceName;
}
public void addAppointment(Appointment appointment) {
if (!appointments.containsValue(appointment)) {
if (!appointments.containsKey(appointment.getStartDate())) {
appointments.put(appointment.getStartDate(), appointment);
}
}
}
public void removeAppointment(Appointment appointment) {
if (appointments.containsValue(appointment)) {
appointments.remove(appointment.getStartDate());
}
}
public boolean join(long transactionID) {
if (currentTransaction != 0) {
return false;
} else {
currentTransaction = transactionID;
return true;
}
}
public void commit(long transactionID) throws TransactionException {
if (currentTransaction != transactionID) {
throw new TransactionException("Invalid TransactionID");
} else {
removeAppointment(currentAppointment);
currentAppointment.setStartDate(updateStartDate);
appointments.put(updateStartDate, currentAppointment);
}
}
public void cancel(long transactionID) {
if (currentTransaction == transactionID) {
currentTransaction = 0;
appointments.remove(updateStartDate);
}
}
public boolean changeDate(long transactionID, Appointment appointment,
Date newStartDate) throws TransactionException {
if ((appointments.containsValue(appointment))
&& (!appointments.containsKey(newStartDate))) {
appointments.put(newStartDate, null);
updateStartDate = newStartDate;
currentAppointment = appointment;
return true;
}
return false;
}
public boolean changeAppointment(Appointment appointment,
Date[] possibleDates,
AppointmentTransactionParticipant[] participants, long transactionID) {
try {
for (int i = 0; i < participants.length; i++) {
if (!participants[i].join(transactionID)) {
return false;
}
}
for (int i = 0; i < possibleDates.length; i++) {
if (isDateAvailable(transactionID, appointment,
possibleDates[i], participants)) {
try {
commitAll(transactionID, participants);
return true;
} catch (TransactionException exc) {
}
}
}
} catch (RemoteException exc) {
}
try {
cancelAll(transactionID, participants);
} catch (RemoteException exc) {
}
return false;
}
private boolean isDateAvailable(long transactionID,
Appointment appointment, Date date,
AppointmentTransactionParticipant[] participants) {
try {
for (int i = 0; i < participants.length; i++) {
try {
if (!participants[i].changeDate(transactionID, appointment,
date)) {
return false;
}
} catch (TransactionException exc) {
return false;
}
}
} catch (RemoteException exc) {
return false;
}
return true;
}
private void commitAll(long transactionID,
AppointmentTransactionParticipant[] participants)
throws TransactionException, RemoteException {
for (int i = 0; i < participants.length; i++) {
participants[i].rumit(transactionID);
}
}
private void cancelAll(long transactionID,
AppointmentTransactionParticipant[] participants)
throws RemoteException {
for (int i = 0; i < participants.length; i++) {
participants[i].cancel(transactionID);
}
}
public String toString() {
return serviceName + " " + appointments.values().toString();
}
}
interface AppointmentTransactionParticipant extends Remote {
public boolean join(long transactionID) throws RemoteException;
public void commit(long transactionID) throws TransactionException,
RemoteException;
public void cancel(long transactionID) throws RemoteException;
public boolean changeDate(long transactionID, Appointment appointment,
Date newStartDate) throws TransactionException, RemoteException;
}
class TransactionException extends Exception {
public TransactionException(String msg) {
super(msg);
}
}