import com.sun.jini.lease.landlord.LandlordLease; import net.jini.core.discovery.LookupLocator; import net.jini.core.entry.Entry; import net.jini.core.event.EventRegistration; import net.jini.core.event.RemoteEventListener; import net.jini.core.event.UnknownEventException; import net.jini.core.lease.Lease; import net.jini.core.lease.UnknownLeaseException; import net.jini.core.lookup.ServiceID; import net.jini.discovery.LookupDiscoveryManager; import net.jini.lookup.JoinManager; import net.jini.lookup.ServiceIDListener; import java.io.*; import java.rmi.MarshalledObject; import java.rmi.RMISecurityManager; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; /** * The Jini server itself; handles joining the Jini community, event registrations, notifications, persisting server data, etc. * * @author Cormac Redmond -- credmond85 /at/ gmail */ public class HealthSpaServer extends UnicastRemoteObject implements HealthSpaInterface, Runnable { private HealthSpa healthSpa; //The JoinManager to use protected JoinManager join = null; //The file used to store persistant data protected File serFile = null; protected List usersRegdForEvents = new ArrayList(); //The serializable data we want to store RegistrationLandlord landlord; protected LandlordLease.Factory factory = new LandlordLease.Factory(); int nextSequenceNumber = 1; protected long lastEventSeqNo = 0; public HealthSpaServer(File serFile, boolean firsttime) throws IOException, ClassNotFoundException { this.serFile = serFile; //Set security manager if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } if (firsttime) register(); else reregister(); //Once complete, create a HealthSpa healthSpa = new HealthSpa(); //The landlord to use landlord = new RegistrationLandlord(usersRegdForEvents, factory); } //Data we want to persist from run to run static class PersistentData implements Serializable { ServiceID serviceID; Entry[] attrs; String[] groups; LookupLocator[] locators; public PersistentData() { } } // An inner class to catch ID changes class IDListener implements ServiceIDListener { public void serviceIDNotify(ServiceID serviceID) { System.out.println("Got service ID " + serviceID); PersistentData state = new PersistentData(); state.serviceID = serviceID; state.attrs = join.getAttributes(); LookupDiscoveryManager luMgr = (LookupDiscoveryManager) join.getDiscoveryManager(); state.groups = luMgr.getGroups(); state.locators = luMgr.getLocators(); try { writeState(state); } catch (IOException ex) { System.err.println("Couldn't write to file: " + ex.getMessage()); ex.printStackTrace(); join.terminate(); System.exit(1); } } } //Register for first time protected void register() throws IOException { if (join != null) { throw new IllegalStateException( "Wrapper already started."); } System.out.println("Registering HealthSpaServer..."); LookupDiscoveryManager luMgr = new LookupDiscoveryManager(new String[]{""}, null, null); join = new JoinManager(getProxy(), null, new IDListener(), luMgr, null); } //Re-register if a restart protected void reregister() throws IOException, ClassNotFoundException { if (join != null) { throw new IllegalStateException( "Wrapper already started."); } PersistentData state = readState(); System.out.println("Restarting HealthSpaServer: old ID is: " + state.serviceID); LookupDiscoveryManager luMgr = new LookupDiscoveryManager(state.groups, state.locators, null); //Rejoin with old attribres, ID, etc join = new JoinManager(getProxy(), state.attrs, state.serviceID, luMgr, null); } //Save state data protected void writeState(PersistentData state) throws IOException { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(serFile)); out.writeObject(state); out.flush(); out.close(); } //Read state data protected PersistentData readState() throws IOException, ClassNotFoundException { ObjectInputStream in = new ObjectInputStream( new FileInputStream(serFile)); PersistentData state = (PersistentData) in.readObject(); in.close(); return state; } protected Object getProxy() { return this; } //Run everytime a user cancels a booking... protected void sendCancellationEvents(int hour, int day, int week, String roomCode) { System.out.println("About to send cancellation events.."); // First, scavenge the list for dead registrations, in // reverse order (to make us immune from compaction) long now = System.currentTimeMillis(); for (int i = usersRegdForEvents.size() - 1; i >= 0; i--) { Registration reg = (Registration) usersRegdForEvents.get(i); if (reg.expiration < now) { System.out.println("Someone has expired"); reg.cancelled(); usersRegdForEvents.remove(i); } } long cancellationEvent = 0; CancellationCookie tempCookie = new CancellationCookie(hour, day, week, roomCode, cancellationEvent); // Now, check all registered listeners and message the interested ones for (int i = 0, size = usersRegdForEvents.size(); i < size; i++) { Registration reg = (Registration) usersRegdForEvents.get(i); CancellationCookie cookie = (CancellationCookie) reg.getCookie(); //If a user is interested in this cancellation if (cookie.hour == hour && cookie.day == day && cookie.week == week && cookie.roomCode.equals(roomCode)) { try { //Create event CancellationEvent ev = new CancellationEvent(getProxy(), cancellationEvent, lastEventSeqNo++, reg.data, hour, day, week, roomCode, healthSpa.rooms.get(roomCode).getName()); System.out.println("Notifying a listener.."); //Send event reg.listener.notify(ev); System.out.println("Notified"); } catch (RemoteException ex) { // Just complain... System.err.println("Error notifying remote listener: " + ex.getMessage()); } catch (UnknownEventException ex) { // Cancel the registration... Here I'll // do this by setting its expiration so // that it'll be dropped the next time // through. System.err.println("Unknown event, dropping: " + ex.getMessage()); reg.expiration = 0; } } } try { //Now cancel all the leases of users who have just been informed System.out.println("Cancelling the leases of those just informed.."); landlord.cancel(tempCookie); } catch (UnknownLeaseException ex) { ex.printStackTrace(); } catch (RemoteException ex) { ex.printStackTrace(); } } static void showUsage() { System.err.println("Usage: HealthSpaServer " + "[-f] serialization_file"); System.exit(1); } public static void main(String[] args) { boolean firsttime = false; String serFileName = null; File serFile = null; if (args.length < 1 || args.length > 2) { // showUsage(); } if (args.length == 2) { // -f means create a new file if (args[0].equals("-f")) { firsttime = true; serFileName = args[1]; } else { showUsage(); } } else { //Filename given serFileName = args[0]; } serFile = new File(serFileName); //serFile = new File("c:\\test.txt"); try { HealthSpaServer healthSpaServer = new HealthSpaServer(serFile, firsttime); new Thread(healthSpaServer).start(); } catch (Exception ex) { ex.printStackTrace(); } } public void run() { while (true) { try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ex) { } } } // Synchronized method to register for receiving notification of cancelled bookings... public synchronized EventRegistration registerForCancellationEvents(int hour, int day, int week, String roomCode, MarshalledObject data, RemoteEventListener listener, long duration) throws RemoteException { // Build a cookie based on the table and token System.out.println("Received a register request..."); long eventType = 0; long sequenceNo = nextSequenceNumber++; //Create a cookie, i.e., holds the event type and time slot person is interested in CancellationCookie cookie = new CancellationCookie(hour, day, week, roomCode, eventType); // Create the lease and registration, and add the // registration to the appropriate list. long expiration = landlord.getExpiration(duration); Registration reg = new Registration(cookie, listener, data, expiration); //Create the lease Lease lease = factory.newLease(cookie, landlord, expiration); //Register user for notification usersRegdForEvents.add(reg); EventRegistration eventRegistration = new EventRegistration(eventType, getProxy(), lease, sequenceNo); System.out.println("Sending back EventRegistration.."); // Return an EventRegistration to the client return eventRegistration; } public boolean[] getRoomTimetableForPeriod(String roomCode, int hour, int week) throws RemoteException { return healthSpa.getRoomTimetableForPeriod(roomCode, hour, week); } public ModelObject userLogin(String username, String password) throws RemoteException { return healthSpa.userLogin(username, password); } public int makeBooking(String roomCode, String memberCode, String staffCode, String username, String password, int hour, int day, int week) throws RemoteException { return healthSpa.makeBooking(roomCode, memberCode, staffCode, username, password, hour, day, week); } public int cancelBooking(Booking tempBooking, String memberCode, String username, String password) throws RemoteException { int result = healthSpa.cancelBooking(tempBooking, memberCode, username, password); //If successfully cancelled if (result == 0) { //Send out relevant cancellation events this.sendCancellationEvents(tempBooking.getHour(), tempBooking.getDay(), tempBooking.getWeek(), tempBooking.getRoomCode()); } return result; } public boolean[][] getStaffRoster(String staffCode, int week) throws RemoteException { return healthSpa.getStaffRoster(staffCode, week); } public int addUser(String username, String password, String newMemberUsername, String newMemberPassword, String newMemberName) throws RemoteException { // this.sendCancellationEvents(); return healthSpa.addUser(username, password, newMemberUsername, newMemberPassword, newMemberName); } public int changePassword(String memberCode, String memberUsername, String memberCurrentPassword, String memberNewPassword) { return healthSpa.changePassword(memberCode, memberUsername, memberCurrentPassword, memberNewPassword); } public LinkedList getMemberList(String adminUsername, String adminPassword) { return healthSpa.getMemberList(adminUsername, adminPassword); } public ArrayList getMemberBookingInfo(String username, String password, String memberCode) { return healthSpa.getMemberBookingInfo(username, password, memberCode); } public int setPrice(String adminUsername, String adminPassword, String roomCode, double newPrice) throws RemoteException { return healthSpa.setPrice(adminUsername, adminPassword, roomCode, newPrice); } public LinkedList getRoomList() throws RemoteException { return healthSpa.getRoomList(); } public int recordPayment(String adminUsername, String adminPassword, String memberCode, int bookingNo) { return healthSpa.recordPayment(adminUsername, adminPassword, memberCode, bookingNo); } public LinkedList getStaffAvailableList(int hour, int day, int week) throws RemoteException { return healthSpa.getStaffAvailableList(hour, day, week); } public LinkedList getStaffList() throws RemoteException { return healthSpa.getStaffList(); } public boolean isUserAdmin(String memberCode) throws RemoteException { return healthSpa.isUserAdmin(memberCode); } }