CS 696 Emerging Technologies: Java Distributed Computing Spring Semester, 1999 Jini Distributed Events |
||
---|---|---|
© 1999, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 13-Apr-99 |
Distributed Events
Jini’s distributed events system allows code in one VM to register to be notified of an event in another VM.
The objects involved in a distributed event system are
Classes & Interfaces
RemoteEventListener
RemoteEvent
EventRegistration
Interface net.jini.core.event.RemoteEventListener
A Remote event listener must be registered with the event generator. The method used to register for an event is not specified by Jini. To be registered for a remote event an object must implement the RemoteEventListener interface. This interface is a Remote interface. This means that the Remote event listener must have a rmi stub (generated by rmic) and be exported (either explicitly or by being a subclass of java.rmi.server.UnicastRemoteObject or java.rmi.activation.Activatable). The event generator notifies the listener of an event by calling the notify method. If the listener does not recognize the type of event sent to it, the listener can throw the UnknownEventException. The event generator should then remove the listener from the list of listeners for that type of event.
public void notify( RemoteEvent theEvent ) throws UnknownEventException, java.rmi.RemoteException
net.jini.core.event.RemoteEvent
A RemoteEvent object contains the following:
RemoteEvent(java.lang.Object source, long eventID, long seqNum, java.rmi.MarshalledObject handback)
getID() getRegistrationObject() getSequenceNumber() getSource()
net.jini.core.event.EventRegistration
When a listener is registered with an event generator, the generator is to return an EventRegistration object. This object contains the following information:
Event registration lease
Event type
Event source
Current event type sequence number
Constructor EventRegistration(long eventID, java.lang.Object source, Lease lease, long seqNum)Methods getID() getLease() getSequenceNumber() getSource()
Distributed Event Example
In this example a single counter object acts as a server. It supports one type of remote event: decrease. When the decrease method of the counter is called all listeners are notified and removed from the listener list.
Classes in Example RemoteCounter
Example Source Code RemoteCounter
package whitney.jini.examples.event; import java.rmi.MarshalledObject; import java.rmi.Remote; import java.rmi.RemoteException; import net.jini.core.event.EventRegistration; import net.jini.core.event.RemoteEventListener; import net.jini.core.lease.LeaseDeniedException; public interface RemoteCounter extends Remote { public abstract int increase() throws RemoteException; public abstract int decrease() throws RemoteException; public abstract int count() throws RemoteException; public EventRegistration addDecreaseListener( RemoteEventListener decreaseListener, MarshalledObject handbackObject ) throws RemoteException, LeaseDeniedException; }
RemoteCounterImpl
package whitney.jini.examples.event; import com.sun.jini.lease.landlord.Landlord; import com.sun.jini.lease.landlord.LandlordLease.Factory; import com.sun.jini.lease.landlord.LandlordLease; import com.sun.jini.lease.landlord.LandlordLeaseFactory; import com.sun.jini.lease.landlord.LeasedResource; import java.rmi.MarshalledObject; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; 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.LeaseDeniedException; public class RemoteCounterImpl extends Thread implements RemoteCounter, Landlord { private static final long DECREASE_ID = 0; private long decreaseEventSequence = 0; private final long maxLeaseDuration = 1000 * 60 * 2; //2 minutes private int leasesCreated = 0; LandlordLeaseFactory leaseFactory = new LandlordLease.Factory(); HashMap activeLeases = new HashMap(); private int count; public RemoteCounterImpl( ) { this( 0); } public RemoteCounterImpl( int initialCount) { count = initialCount; } public String toString() { return "Counter(" + count + ")"; } // Count interface methods public int count() { return count; } public int increase() { printMessage( "Increase called" ); return ++count; } public int decrease() { decreaseEventSequence++; printMessage( "Decrease called" ); notifyDecreaseListeners(); return --count; }
RemoteCounterImpl – Counter Methods
public EventRegistration addDecreaseListener( RemoteEventListener decreaseListener, MarshalledObject handbackObject ) throws RemoteException, LeaseDeniedException { printMessage( "Register listener" ); Object cookie = newLeaseCookie(); long leaseExpiration = maxLeaseDuration + now(); Lease eventLease = leaseFactory.newLease( cookie, this, leaseExpiration ); printMessage( "Create EventRegistration" ); EventRegistration decreaseReg = new EventRegistration( DECREASE_ID, this, eventLease, decreaseEventSequence); LeasedEventData eventData = new LeasedEventData( leaseExpiration,decreaseListener, handbackObject ); activeLeases.put( cookie, eventData ); printMessage( "End register listener" ); return decreaseReg; }
RemoteCounterImpl – Notify Listeners
private void notifyDecreaseListeners() { printMessage( "Start notify of event" ); long now = now(); Map clonedMap; synchronized ( activeLeases ) { clonedMap = (Map) activeLeases.clone(); activeLeases.clear(); } printMessage( "Cloned map" ); Iterator listeners = clonedMap.values().iterator(); while ( listeners.hasNext() ) { LeasedEventData eventInfo = (LeasedEventData) listeners.next(); try { printMessage( "Send notify" ); if ( eventInfo.getExpiration() > now ) eventInfo.notifyListener(DECREASE_ID, this, decreaseEventSequence ); } catch ( UnknownEventException doesntKnow ) { printMessage("UnknownEventException" + doesntKnow.getMessage()); } catch ( RemoteException rmiProblem) { printMessage( "RemoteException" + rmiProblem.getMessage() ); } } }
RemoteCounterImpl – Landlord Methods
// Landlord methods public void cancel(java.lang.Object cookie) { printMessage( "Cancel called" ); if ( !activeLeases.containsKey( cookie ) ) return; synchronized ( activeLeases ) { activeLeases.remove( cookie ); } } public void cancelAll(java.lang.Object[] cookie) { for ( int k = 0; k < cookie.length; k++ ) cancel( cookie[k] ); } public long renew(java.lang.Object cookie, long extension) throws LeaseDeniedException { printMessage( "Renew called " + extension ); synchronized ( activeLeases ) { if ( !activeLeases.containsKey( cookie ) ) throw new LeaseDeniedException( "Requested resourse does not exist"); long newDuration = Math.min( extension, maxLeaseDuration ); LeasedEventData eventLease = (LeasedEventData) activeLeases.get( cookie); eventLease.setDuration( newDuration ); return newDuration; } }
RemoteCounterImpl – Landlord Methods Continued
public Landlord.RenewResults renewAll(java.lang.Object[] cookie, long[] extension) { long renewalGranted[] = new long[ cookie.length ]; Exception renewalDenied[] = new Exception[ cookie.length ]; for ( int k = 0; k < cookie.length; k++ ) try { renewalGranted[k] = renew( cookie[k], extension[k] ); renewalDenied[k] = null; } catch (LeaseDeniedException deny ) { renewalGranted[k] = -1; renewalDenied[k] = deny; } return new Landlord.RenewResults(renewalGranted, renewalDenied ); }
RemoteCounterImpl – Check for expired leases
public void run() { printMessage ( "Run called" ); try { while (true) { Thread.sleep( maxLeaseDuration/2 ); printMessage ( "CheckAllLeases" ); checkAllLeases(); } } catch (InterruptedException threadInterupted ) { } } private void checkAllLeases() { synchronized ( activeLeases ) { Iterator counterKeys = activeLeases.keySet().iterator(); while (counterKeys.hasNext() ) { Object key = counterKeys.next(); if ( hasLeaseExpired( key ) ) { counterKeys.remove(); printMessage( "lease expired " + key ); } } } } private boolean hasLeaseExpired( Object cookie ) { LeasedEventData aCounter = (LeasedEventData) activeLeases.get( cookie); long expiration = aCounter.getExpiration( ); printMessage( "lease check " + cookie + " duration " + (expiration - now()) ); if ( now() >= expiration ) return true; else return false; }
RemoteCounterImpl – Helper methods
private Integer newLeaseCookie() { return new Integer( leasesCreated++); } private long now() { return System.currentTimeMillis(); } private void printMessage( String message ) { System.out.println( toString()+ " " + message); } }
LeasedEventData
package whitney.jini.examples.event; import java.rmi.MarshalledObject; import java.rmi.RemoteException; import java.util.List; import java.util.Map; import net.jini.core.event.RemoteEvent; import net.jini.core.event.RemoteEventListener; import net.jini.core.event.UnknownEventException; public class LeasedEventData { MarshalledObject handbackObject; RemoteEventListener eventListener; long leaseExpiration; public LeasedEventData( long leaseExpiration, RemoteEventListener eventListener, MarshalledObject handbackObject ) { this.leaseExpiration = leaseExpiration; this.handbackObject = handbackObject; this.eventListener = eventListener; } public long getExpiration() { return leaseExpiration; } public void setExpiration( long newLeaseExpiration) { leaseExpiration = newLeaseExpiration; } public void setDuration( long newLeaseDuration) { setExpiration( newLeaseDuration + System.currentTimeMillis() ); } public void notifyListener( long eventID, Object eventSource, long eventSequenceNumber ) throws UnknownEventException, RemoteException { RemoteEvent eventData = new RemoteEvent( eventSource, eventID, eventSequenceNumber, handbackObject ); eventListener.notify( eventData ); } }
RegisterCounter
package whitney.jini.examples.event; import com.sun.jini.lease.LeaseRenewalManager; import java.rmi.RMISecurityManager; import java.rmi.server.UnicastRemoteObject; import net.jini.core.discovery.LookupLocator; import net.jini.core.entry.Entry; import net.jini.core.lease.Lease; import net.jini.core.lookup.ServiceID; import net.jini.core.lookup.ServiceItem; import net.jini.core.lookup.ServiceRegistrar; import net.jini.core.lookup.ServiceRegistration; import net.jini.lookup.entry.Name; public class RegisterCounter { public static void main (String[] args) throws Exception { System.setSecurityManager (new RMISecurityManager ()); LookupLocator lookup = new LookupLocator ("jini://eli.sdsu.edu"); ServiceRegistrar registrar = lookup.getRegistrar (); Entry[] serverAttributes = new Entry[1]; serverAttributes[0] = new Name ("CounterEvent"); ServiceID serverID = null; System.out.println ( "Create server" ); RemoteCounterImpl theServer = new RemoteCounterImpl(); theServer.start(); System.out.println ( "Server is started" ); RemoteCounter serverStub = (RemoteCounter) UnicastRemoteObject.exportObject( theServer ); System.out.println ( "Server exported" ); ServiceItem countServer = new ServiceItem( serverID, serverStub, serverAttributes); System.out.println ( "Start Jini registration" ); ServiceRegistration serverReg = registrar.register(countServer, 1000 * 60 * 5 ); System.out.println ( "Server registered" ); Lease serverLease = serverReg.getLease(); LeaseRenewalManager manageLease = new LeaseRenewalManager( serverLease, Lease.FOREVER, null ); System.out.println ( "Server lookup lease handled" ); Thread.sleep( 1000 * 60 * 7 ); // Server is just for testing, kill it to save the effort of manual killing it System.out.println ( "Server going down: "); manageLease.cancel(serverLease); //just to be polite UnicastRemoteObject.unexportObject( theServer, true ); System.exit( 0 ); } }
RemoteCounterClient
package whitney.jini.examples.event; import java.io.IOException; import java.rmi.RemoteException; import java.rmi.RMISecurityManager; import net.jini.core.discovery.LookupLocator; import net.jini.core.entry.Entry; import net.jini.core.entry.Entry; import net.jini.core.lookup.ServiceRegistrar; import net.jini.core.lookup.ServiceTemplate; import net.jini.core.lookup.ServiceTemplate; import net.jini.lookup.entry.Name; public class RemoteCounterClient extends Thread { RemoteCounter myCount; String name; public static void main (String[] args) throws Exception { if (args.length == 0 ) { System.out.println( "Usage: java CounterEventClient clientName"); System.exit(0); } RemoteCounterClient aClient = new RemoteCounterClient(args[0], "jini://eli.sdsu.edu", "CounterEvent" ); aClient.start(); } public RemoteCounterClient(String name, String lookupService, String serviceName) throws IOException, RemoteException, ClassNotFoundException { this.name = name; System.setSecurityManager (new RMISecurityManager ()); LookupLocator lookup = new LookupLocator ( lookupService ); ServiceRegistrar registrar = lookup.getRegistrar (); Entry[] serverAttributes = new Entry[1]; serverAttributes[0] = new Name ( serviceName ); ServiceTemplate template = new ServiceTemplate (null, null, serverAttributes); myCount = (RemoteCounter) registrar.lookup (template); printMessage( "Have a counter" ); }
RemoteCounterClient - Continued public void run() { printMessage( "Start run method" ); try { for ( int k = 0; k < 5; k ++ ) callCounter(); } catch (Exception threadKilled ) { printMessage( "Exiting on exception: " + threadKilled.getMessage() ); } printMessage( "Exiting" ); } private void callCounter() throws InterruptedException, RemoteException { for ( int k = 0; k < 5; k ++ ) { myCount.increase(); sleep( 1000 * 4 ); } printMessage( "Calling decrease" ); myCount.decrease(); } private void printMessage( String message ) { System.out.println( "CounterClient " + name + " " + message); } }
CounterEventClient
package whitney.jini.examples.event; import com.sun.jini.lease.LeaseListener; import com.sun.jini.lease.LeaseRenewalEvent; import com.sun.jini.lease.LeaseRenewalManager; import java.io.IOException; import java.rmi.MarshalledObject; import java.rmi.RemoteException; import java.rmi.RMISecurityManager; import java.rmi.server.UnicastRemoteObject; import net.jini.core.discovery.LookupLocator; import net.jini.core.entry.Entry; import net.jini.core.event.EventRegistration; import net.jini.core.event.RemoteEvent; import net.jini.core.event.RemoteEventListener; import net.jini.core.event.UnknownEventException; import net.jini.core.lease.Lease; import net.jini.core.lease.LeaseDeniedException; import net.jini.core.lookup.ServiceRegistrar; import net.jini.core.lookup.ServiceTemplate; import net.jini.lookup.entry.Name; import net.jini.core.lease.LeaseMap; import net.jini.core.lease.LeaseException; public class CounterEventClient extends Thread implements LeaseListener, RemoteEventListener { RemoteCounter myCount; String name; EventRegistration decreaseRegistration; LeaseMap leases; long leaseDuration; public static void main (String[] args) throws Exception { if (args.length == 0 ) { System.out.println( "Usage: java CounterEventClient clientName"); System.exit(0); } CounterEventClient aClient = new CounterEventClient(args[0], "jini://eli.sdsu.edu", "CounterEvent" ); aClient.start(); } // RemoteEventListener method public void notify(RemoteEvent theEvent) throws UnknownEventException, RemoteException { if (theEvent.getID() != decreaseRegistration.getID() ) throw new UnknownEventException("Do not notify me of these events"); printMessage( "Received decrease event " + theEvent.getID() ); printMessage( "Count after decrease is " + myCount.count() ); leases.remove( decreaseRegistration.getLease() ); registerForRemoteEvent(); }
CounterEventClient - Continued //LeaseListener method public void notify(LeaseRenewalEvent lostLease) { Exception deniedReason = lostLease.getException(); if (deniedReason != null ) printMessage( "Lost lease because: " + deniedReason ); else printMessage( "Lost lease, no reason available" ); } private void registerForRemoteEvent() throws RemoteException { printMessage( "Start registration for decrease event " ); try { MarshalledObject handbackObject = new MarshalledObject( "Not used" ); decreaseRegistration = myCount.addDecreaseListener( this, handbackObject ); Lease eventLease = decreaseRegistration.getLease(); long leaseDuration = eventLease.getExpiration() - now() - 100; leases = eventLease.createLeaseMap(leaseDuration ); printMessage( "Lease duration " + leaseDuration ); printMessage( "Registered for decrease event " ); } catch (LeaseException noRegistration ) { printMessage( "Registration for decrease event refused" ); } catch (IOException marshallingProblem ) { printMessage( "Marshalling problem" ); } }
CounterEventClient - Continued
public CounterEventClient(String name, String lookupService, String serviceName) throws IOException, RemoteException, ClassNotFoundException { this.name = name; System.setSecurityManager (new RMISecurityManager ()); LookupLocator lookup = new LookupLocator ( lookupService ); ServiceRegistrar registrar = lookup.getRegistrar (); Entry[] serverAttributes = new Entry[1]; serverAttributes[0] = new Name ( serviceName ); ServiceTemplate template = new ServiceTemplate (null, null, serverAttributes); myCount = (RemoteCounter) registrar.lookup (template); printMessage( "Have a counter" ); RemoteEventListener notifyMe = (RemoteEventListener) UnicastRemoteObject.exportObject( this ); registerForRemoteEvent(); } private long leaseDuration() { return decreaseRegistration.getLease().getExpiration() – System.currentTimeMillis(); }
CounterEventClient - Continued // Maintain the lease public void run() { while ( isLeaseValid() ) { try { sleep( 1000 * 30 ); leases.renewAll(); printMessage( "Lease duration: " + leaseDuration() ); } catch (LeaseException denied ) { printMessage( "Lease renewal denied. Reason give: " + denied.getMessage() ); } catch (Exception threadKilled ) { printMessage( "Exiting on exception: " + threadKilled.getMessage() ); break; } } } private boolean isLeaseValid() { if (leaseDuration() > 0 ) return true; else return false; } private void printMessage( String message ) { System.out.println( "CounterClient " + name + " " + message); } private long now() { return System.currentTimeMillis(); } }
Copyright ©, All rights reserved.
1999 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.
Previous    visitors since 13-Apr-99    Next