throbber
Chapter 13
`
`Leased F1'1eC1ass1'fier
`
`A dynamically extensible version of a file classifier will have methods to add and
`remove MIME mappings:
`
`package common;
`
`import java.io.Serializable;
`
`/>|<*
`
`* LeaseFileClassifier.java
`*/
`
`import net.jini.core.1ease.Lease;
`
`public interface LeaseFileClassifier extends Serializable {
`
`public MIMEType getMIMEType(String fileName)
`throws java.Imi.RemoteException;
`
`Add the MIME type for the given suffix.
`The suffix does not contain '.' e.g. "gif”.
`
`@exception net.jini.core.lease.LeaseDeniedException
`a previous MIME type for that suffix exists.
`This type is removed on expiration or cancellation
`of the lease.
`
`*/
`
`public Lease addType(String suffix, MIMEType type)
`throws java.Imi.RemoteException,
`net.jini.core.lease.LeaseDeniedException;
`
`/*>l<
`
`* Remove the MIME type for the suffix.
`
`*/
`
`public void removeType(String suffix)
`throws java.rmi.RemoteException;
`} // LeaseFileClasssifier
`
`The addType() method returns a lease. We shall use the landlord leasing system
`discussed in Chapter 7. The client and the service will be in different ]avaVMs, prob-
`ably on different machines. Figure 13-5 gives the object structure on the service side.
`
`251
`
`

`
`This should be compared to Figure 7-3 where we considered the “foo” implementa-
`tion of landlord leasing.
`
`More Complex Examples
`
`1 Fileclassifier
`Server
`
`Fileclassifier
`Leasedfiesource
`
`Figure 13-5. Class diagram for leasing on the server
`
`On the client side, the lease object will be a copy of the lease created on the
`
`server (normally RM1 semantics), but the other objects from the service will be
`
`stubs that call into the real objects on the service. This is shown in Figure 13-6.
`
`TestFile
`ClassifierLease
`
`Fileclassifier
`
`|mpl_Stub
`
`7
`
`7
`
`Fileclassifier
`
`Land|ord_Stub
`
`Figure 13-6. Class diagram for leasing on the client
`
`252
`
`

`
`The FileClassifierLeasedResource Class
`
`The FileclassifierLeasedResource class acts as a wrapper around the actual
`resource, adding cookie and time expiration fields around the resource. It adds a
`
`unique cookie mechanism, in addition to making the wrapped resource visible.
`
`/>i<*
`
`* FileClassifierLeasedResource.java
`*/
`
`package lease;
`
`import common.LeaseFileClassifier;
`
`import com.sun.jini.lease.landlord.LeasedResource;
`
`public class FileClassifierLeasedResource implements LeasedResource
`
`{
`
`static protected int cookie = 0;
`protected int thisCookie;
`protected LeaseFileClassifier fi1eClassifier;
`protected long expiration = 0;
`protected String suffix = null;
`
`public FileClassifierLeasedResource(LeaseFileClassifier fileclassifier;
`String suffix) {
`this.fileClassifier = fileClassifier;
`this.suffix = suffix;
`thiscookie = cookie++;
`
`public void setExpiration(long newExpiIation)
`this.expiration = newExpiration;
`
`{
`
`}
`
`public long getExpiration() {
`return expiration;
`
`} p
`
`ublic Object getCookie() {
`return new Integer(thisCookie);
`
`public LeaseFileClassifier getFileClassifier() {
`return fileclassifier;
`
`CWapwr13
`
`253
`
`

`
`More Complex Examples
`
`public String getSuffix() {
`return suffix;
`
`}
`
`} // FileClassifierLeasedResource
`
`The Fi1eClassifierLeaseManager Class
`
`The FileClassifierLeaseManager class is Very similar to the code given for the
`FooLeaseManager in Chapter 7:
`
`/*>i<
`
`* FileClassifierLeaseManager.java
`*/
`
`package lease;
`
`import java.util.*;
`import common.LeaseFileClassifier;
`
`import net.jini.core.lease.Lease;
`
`import com.sun.jini.lease.landlord.LeaseManager;
`import com.sun.jini.lease.landlord.LeasedResource;
`
`import com.sun.jini.1ease.landlord.LeaseDurationPolicy;
`import com.sun.jini.lease.landlord.Landlord;
`import com.sun.jini.1ease.landlord.LandlordLease;
`
`import com.sun.jini.lease.land1ord.LeasePolicy;
`
`public class FileClassifierLeaseManager implements LeaseManager {
`
`protected static long DEFAULT_TIME = 30*1000L;
`
`protected Vector tileClassifierResources = new Vector();
`protected LeaseDurationPolicy policy;
`
`public FileClassifierLeaseManager(Landlord landlord) {
`policy = new LeaseDurationPolicy(Lease.FOREVER,
`DEFAULT_TIME,
`landlord,
`this,
`
`new LeaseReaper().start();
`
`new LandlordLease.Factory());
`
`public void register(LeasedResource r,
`
`long duration) {
`
`254
`
`

`
`Chapter 13
`
`tileClassitierResouIces.add(r);
`
`} p
`
`long duration,
`ublic void renewed(LeasedResource I,
`// no smarts in the scheduling, so do nothing
`
`long olddur)
`
`{
`
`} p
`
`ublic void cancelAl1(Object[] cookies) {
`for (int n = cookies.1ength;
`--n >= 0;
`cancel(cookies[ ]);
`
`)
`
`{
`
`public void cancel(0bject cookie) {
`{
`)
`for (int n = fileC1assitierResources.si2e(); --n >= 0;
`FileClassitierLeasedResource I = (FileClassitierLeasedResource)
`fileClassifieIResources.elementAt(n);
`
`if (Ipo1icy.ensureCurrent(r)) {
`System.out.println("Lease expired for cookie = " +
`r.getCookie());
`
`try {
`r.getFileClassitieI().removeType(r.getSuffix());
`} catch(java.Imi.RemoteException e) {
`e.printStackTrace();
`
`} f
`
`i1eClassifierResources.removeElementAt(n);
`
`public LeasePolicy getPolicy() {
`return policy;
`
`} p
`
`ublic LeasedResource getResource(0bject cookie) {
`) {
`for (int n = fileClassifierResources.size()3 ~-n >= 0;
`FileClassifierLeasedResource I = (FileClassitierLeasedResource)
`fileC1assifierResources.elementAt(n);
`
`if (r.getCookie().equals(cookie)) {
`return I;
`
`} r
`
`eturn null;
`
`255
`
`

`
`More Complex Examples
`
`class Leasekeaper extends Thread {
`public void run() {
`while (true) {
`try {
`Thread.s1eep(DEFAULT_TIME)
`
`;
`
`} c
`
`atch (InterruptedException e) {
`
`} f
`
`n >= 0; n—-) {
`or (int n = fileClassifierResources.size()—1;
`Fi1eClassifierLeasedResource r = (FileclassifierLeasedResource)
`fileClassifierResources.elementAt(n)
`
`if (Ipolicy.ensuIeCurrent(r)) {
`System.out.println("Lease expired for cookie = " +
`r.getCookie()) ;
`
`try {
`
`r.getFileClassifier().removeType(r.getSuffix())5
`} catch(java.rmi.RemoteException e)
`{
`e.printStackTrace();
`
`} f
`
`ileC1assifierResouIces.removeElementAt(n);
`
`} // FileClassifierLeaseManager
`
`The F1'leClass1'f1'erLand1ord Class
`
`The Fi1eClassifierLandlord class is very similar to the FooLandlord in Chapter 7:
`
`/**
`
`* FileClassifierLandloId.java
`*/
`
`package lease;
`
`import common.LeaseFileClassifier;
`
`256
`
`

`
`Chapwr13
`
`import com.sun.jini.lease.landlord.*;
`
`import net.jini.core.lease.LeaseDeniedException;
`import net.jini.core.lease.Lease;
`import java.rmi.server.UnicastRemote0bject;
`import java.rmi.Remote;
`
`public class FileClassitierLandlord extends UnicastRemoteObject
`lord, Remote {
`
`implements Land-
`
`FileClassitierLeaseManager manager = null;
`
`public FileClassifierLandlord() throws java.rmi.RemoteException {
`manager = new FileClassifierLeaseManager(this);
`
`public void cancel(0bject cookie) {
`manager.cancel(cookie);
`
`public void cancelAll(Object[] cookies) {
`manager.cancelAll(cookies);
`
`public long renew(java.lang.Object cookie,
`long extension)
`throws net.jini.core.lease.LeaseDeniedException,
`net.jini.core.lease.UnknownLeaseException {
`LeasedResource resource = manager.getResource(cookie);
`if (resource != null) {
`return manager.getPolicy().renew(resource, extension);
`
`} r
`
`eturn -1;
`
`public Lease newFileClassifierLease(LeaseFileClassifier tileClassifier,
`String sutfixKey,
`long duration)
`
`throws LeaseDeniedException {
`FileclassifierLeasedResource r = new
`
`Fi1eClassifierLeasedResource(tileClassitier,
`
`return manager.getPolicy().leaseFor(r, duration);
`
`suffixKey);
`
`public Landlord.RenewResults renewAll(java.lang.Object[] cookie,
`
`257
`
`

`
`long[] extension) {
`
`More Complex Examples
`
`return null;
`
`}
`
`} // FileClassifierLandloId
`
`Summary
`
`Iini provides a framework for building distributed applications. Nevertheless,
`there is still room for variation in how services and clients are written, and some of
`
`these are better than others. This chapter has looked at some of the variations that
`can occur and how to deal with them.
`
`258
`
`

`
`CHAPTER 14
`
`Remote Events
`
`COMPONENTS OF A SYSTEM CAN CHANGE STATE and may need to inform other compo-
`nents that this change has happened. Java Beans and user—interface elements such
`
`as AWT or Swing objects use events to signal these changes. Iini also has an event
`
`mechanism, and this chapter looks at the distributed event model that is part of
`Jini. It looks at how remote event listeners are registered with objects, and how
`
`these objects notify their listeners of changes. Event listeners may disappear, and
`so the Iini event mechanism uses leases to manage listener lists.
`
`This chapter also looks at how leases are managed by event sources. Finally,
`
`we’ll look at how events can be used by applications to monitor when services are
`
`registered or discarded from service locators.
`
`Event Models
`
`lava has a number of event models, differing in various subtle ways. All of these
`
`involve an object (an eventsource) generating an event in response to some
`change of state, either in the object itself (for example, if someone has changed a
`field), or in the external environment (such as when a user moves the mouse). At
`
`some earlier stage, a listener (or set of listeners) will have registered interest in this
`
`event. When the event source generates an event, it will call suitable methods on
`the listeners with the event as parameter. The event models all have their origin
`
`in the Observer pattern from Design Patterns, by Eric Gamma et al., but this is mod-
`
`ified by other pressures, such as Iava Beans.
`There are low—level input events, which are generated by user actions when
`they control an application with a graphical user interface. These events—of type
`
`KeyEvent and MouseEvent—are placed in an event queue. They are removed from the
`queue by a separate thread and dispatched to the relevant objects. In this case, the
`object that is responsible for generating the event is not responsible for dispatching
`it to listeners, and the creation and dispatch of events occurs in different threads.
`Input events are a special case caused by the need to listen to user interactions
`
`and always deal with them without losing response time. Most events are dealt
`
`with in a simpler manner: an object maintains its own list of listeners, generates its
`
`own events, and dispatches them directly to its listeners. In this category fall all the
`semantic events generated by the AWT and Swing toolkits, such as ActionEvent,
`ListSe1ectionEvent, etc. There is a large range of these event types, and they all call
`
`259
`
`

`
`Chapwr14
`
`different methods in the listeners, based on the event name. For example, an
`
`ActionEvent is used in a listener’s actionPeItormed () method of an ActionListeneI.
`
`There are naming conventions involved in this, specified by Java Beans.
`Java Beans is also the influence behind Propertychange events, which get deliv-
`ered whenever a Bean changes a “bound" or “constrained” property value. These
`are delivered by the event source calling the listener’s Propertychange Listener's
`propeItyChange() method or the VetoableChangeListeneI’s vetoab1eChange()
`method. These are usually used to signal a change in a field of an object, where this
`change may be of interest to the listeners either for information or for vetoing.
`Iini objects may also be interested in changes in other Iini objects, and might
`like to be listeners for such changes. The networked nature of Jim has led to a
`particular event model that differs slightly from the other models already in Java.
`The differences are caused by several factors:
`
`9' Network delivery is unreliable—rnessages may be lost. Synchronous methods
`requiring a reply may not work here.
`
`° Network delivery is time—dependent—messages may arrive at different
`times to different listeners. As a result, the state of an object as perceived by
`a listener at any time may be inconsistent with the state of that object as
`perceived by others. Passing complex object state across the network may
`be more complex to manage than passing simpler information.
`
`9 A remote listener may have disappeared by the time the event occurs.
`Listeners have to be allowed to time out, like services do.
`
`Java Beans can require method names and event types that vary and can use
`many classes. This requires a large number of classes to be available across
`the network, which is more complex than a single class with a single method
`with a single event type as parameter (the original Observer pattern used a
`single class with only one method, for simplicity).
`
`Remote Events
`
`Unlike the large number of event classes in the AWT and Swing, for example, Iini uses
`events of one type, the RemoteEvent, or a small number of subclasses of RemoteEvent.
`The RemoteEvent class has these public methods (and some inherited methods):
`
`package net.jini.core.event;
`
`public class RemoteEvent
`public long getID();
`public long getSequenceNumbeI();
`
`implements java.io.Serializable {
`
`260
`
`

`
`Remote Events
`
`public java .Imi .MaIshal1ed0bject getkegistrationobject ( );
`
`Events in Beans and AWT convey complex object state information, and this is
`
`enough for the listeners to act with full knowledge of the changes that have caused
`the event to be generated. Iini events avoid this, and convey just enough informa-
`tion to allow state information to be found if needed. A remote event is serializable
`and is moved around the network to its listeners. The listeners then have to decide
`
`whether or not they need more detailed information than the simple information
`
`in each remote event. If they do need more information, they will have to contact
`the event source to get it.
`AWT events, such as MouseEvent, contain an id field that is set to values such as
`
`MOUSE_PRESSED or MOUSE_RELEASED. These are not seen by the AWT programmer
`because the AWT event dispatch system uses the id field to choose appropriate
`methods, such as mousePressed () or mouseReleased ( ). Iini does not make these
`
`assumptions about event dispatch, and just gives you the identifier. Either the
`source or the listener (or both) will know what this value means. For example, a file
`
`classifier that can update its knowledge of MIME types could have message types
`
`ADD_TYPE and REMOVE_TYPE to reflect the sort of changes it is going through.
`In a synchronous system with no losses, both sides of an interaction can keep
`consistent ideas of state and order of events. In a network system this is not so
`
`easy. Iini makes no assumptions about guarantees of delivery and does not even
`assume that events are delivered in order. The Jim event mechanism does not
`
`specify how events get from producer to listener—it could be by RMI calls, but it
`may be through an unreliable third party. The event source supplies a sequence
`number that could be used to construct state and ordering information if needed,
`
`and this generalizes things such as time—stamps on mouse events. For example, a
`message with id of ADD_TYPE and sequence number of 10 could correspond to the
`
`state change “added MIME type text/xml for files with suffix .xml.” Another event
`
`with id of REMOVE_TYPE and sequence number of 11 would be taken as a later event,
`even if it arrived earlier. The listener will receive the event with id and sequence
`
`number only. Either this will be meaningful to the listener, or it will need to contact
`the event source and ask for more information about that sequence number. The
`
`event source should be able to supply state information upon request, given the
`sequence number.
`
`An idea borrowed from systems such as the Xt Intrinsics and Motif is called
`handback data. This is a piece of data that is given by the listener to the event
`
`source at the time it registers itself for events. The event source records this hand-
`back and then returns it to the listener with each event. This handback can be a
`
`reminder of listener state at the time of registration.
`This can be a little difficult to understand at first. The listener is basically
`
`saying to the event source that it wants to be told whenever something interesting
`happens, but when that does happen, the listener may have forgotten why it was
`
`261
`
`

`
`CWapwr14
`
`interested in the first place, or what it intended to do with the information. So the
`listener also the gives the event source some extra information that it wants
`returned as a “reminder.”
`
`For example, a lini taxi-driver might register interest in taxi—booking events
`from the base station while passing through a geographical area. It registers itself
`as a listener for booking events, and as part of its registration, it could include its
`current location. Then, when it receives a booking event, it is told its old location,
`and it could check to see if it is still interested in events from that old location. A
`
`more novel possibility is that one object could register a different object for events,
`so your stockbroker could register you for events about stock movements, and
`when you receive an event, you would also get a reminder about who registered
`your interest (plus a request for commission...).
`
`Event Registration
`
`Iini does not say how to register listeners with objects that can generate events.
`This is unlike other event models in Java that specify methods, like this
`
`public void addActionListener(Actionlistener listener);
`
`for ActionEvent generators. What Iini does do is to specify a convenience class as a
`return value from this registration. This is the convenience class Eventkegistrat ion:
`
`package net.jini.core.event;
`import net . j ini . core . lease . Lease;
`
`public class EventRegistIation implements java.io.Serializable {
`public EventRegistration(long eventID, Object source,
`Lease lease,
`long seqNum);
`
`public long getID();
`public Object getSouIce();
`public Lease getLease();
`public long getSequenceNumber();
`
`This return object contains information that may be of value to the object that
`registered a listener. Each registration will typically only be for a limited amount of
`time, and this information may be returned in the Lease object. If the event regis-
`tration was for a particular type, this may be returned in the id field. A sequence
`number may also be given. The meaning of these values may depend on the par-
`ticular system—in other words, Iini gives you a class that is optional in use, and
`whose fields are not tightly specified. This gives you the freedom to choose your
`own meanings to some extent.
`
`262
`
`

`
`Remote Events
`
`This means that as the programmer of a event producer, you have to define
`
`(and implement) methods such as these:
`
`public EventRegistration addRemoteEventListener(RemoteEventListener listener);
`
`There is no standard interface for this.
`
`Listener List
`
`Each listener for remote events must implement the RemoteEventListener
`interface:
`
`public interface RemoteEventListener
`java.util.EventListener {
`extends java.rmi.Remote,
`public void notity(RemoteEvent theEvent)
`throws UnknownEventException,
`java.rmi.RemoteException;
`
`Because it extends Remote, the listener will most likely be something like an RMI
`stub for a remote object, so that calling notify() will result in a call on the remote
`
`object, with the event being passed across to it.
`
`In event generators, there are multiple implementations for handling lists of
`
`event listeners all the way through the Java core and extensions. There is no public
`API for dealing with event—listener lists, and so the programmer has to reinvent (or
`copy) code to pass events to listeners. There are basically two cases:
`
`° Only one listener can be in the list.
`
`- Any number of listeners can be in the list.
`
`Single Listener
`
`The case where there is only one listener allowed in the list can be implemented by
`using a single-valued variable, as shown in Figure 14— 1.
`This is the simplest case of event registration:
`
`protected RemoteEventListener listener = null;
`
`public Eventkegistration addRemoteListener(RemoteEventListener listener)
`throws java.util.TooManyListenersException {
`
`263
`
`

`
`Chapter 1 4
`
`listener
`
`_
`RemoteEventLIstener
`
`Figure 14-1. A single listener‘
`
`if (this.listener == null {
`this.listener = listener;
`
`} else {
`
`throw new java.util.TooManyListenersException();
`
`} r
`
`eturn new EventRegistration(0L, this, null, OL);
`
`This is close to the ordinary Java event registration—no really useful informa-
`
`tion is returned that wasn’t known before. In particular, there is no lease object, so
`
`you could probably assume that the lease is being granted ‘‘forever,’' as would be
`the case with non—networked objects.
`When an event occurs, the listener can be informed by the event generator
`
`calling fireNot ify( ):
`
`protected void tireNotity(long eventID,
`long seqNum)
`if (listener == null) {
`return;
`
`{
`
`RemoteEvent remoteEvent = new RemoteEvent(this, eventID,
`
`seqNum, null);
`
`listener. notify(remoteEvent);
`
`It is easy to add a handback to this: just add another field to the object, and set
`and return this object in the registration and notify methods. Far more complex is
`
`adding a non—null lease. Firstly, the event source has to decide on a lease policy,
`that is, for what periods of time it will grant leases. Then it has to implement a
`
`timeout mechanism to discard listeners when their leases expire. And finally, it has
`
`to handle lease renewal and cancellation requests, possibly using its lease policy
`again to make decisions. The landlord package would be of use here.
`
`264
`
`

`
`Remote Events
`
`Multiple Listeners
`
`For the case where there can be any number of listeners, the convenience class
`
`javax. swing.event . Eventtistenerlist can be used. The object delegates some of
`the list handling to the convenience class, as shown in Figure 14-2.
`
`EventGenerator
`
`addRemoteListener()
`
`Figure 14-2. Multiple listeners
`
`|istenerList
`
`_
`v RemoteEventL|stener
`
`A version of event registration suitable for ordinary events is as follows:
`
`import javax.swing.event.EventListenerList;
`
`EventListenerList listenerList = new EventListenerList();
`
`public EventRegistration addRemoteListener(RemoteEventListener l) {
`listenerList.add(RemoteListener.class, 1);
`return new EventRegistration(0L, this, null, OL);
`
`public void removeRemoteListener(RemoteEventListener l) {
`listenerList.remove(RemoteListener.class, l);
`
`// Notify all listeners that have registered interest for
`// notification on this event type.
`The event instance
`// is lazily created using the parameters passed into
`// the fire method.
`
`protected void fireNotify(long eventID,
`long seqNum)
`RemoteEvent remoteEvent = null;
`
`{
`
`// Guaranteed to return a non-null array
`Object[] listeners = listenerList.getListenerList();
`
`// Process the listeners last to first, notifying
`// those that are interested in this event
`
`265
`
`

`
`{
`n -= 2)
`for (int n = listeners.1ength — 2; n >= 0;
`if (listeners[n] == RemoteEventListener.class) {
`RemoteEventListener listener =
`
`(RemoteEventListener) listeners[n+1];
`if (IemoteEvent == null) {
`remoteEvent = new RemoteEvent(this, eventID,
`
`seqNum, null);
`
`{
`
`} t
`
`ry {
`listener.notify(remoteEvent);
`} catch(UnknownEventException e)
`
`e . pIintStackTIace();
`} catch(java.rmi.RemoteException e) {
`e.printStackTrace();
`
`Chapmr14
`
`} I
`
`n this case, a source object need only call fireNotify() to send the event to all
`listeners. (You may decide that it is easier to simply use a Vector of listeners.)
`
`It is again straightforward to add hanclbacks to this. The only tricky point is
`that each listener can have its own handback, so they will need to be stored in
`some kind of map (say a HashMap) keyed on the listener. Then, before not ify() is
`called for each listener, the handback will need to be retrieved for the listener and
`a new remote event created with that handback.
`
`Listener Source
`
`The ordinary Java event model has all objects in a single address space, so that
`registration of event listeners and notifying these listeners all takes place using
`objects in the one space. We have already seen that this is not the case with Jim.
`Jim is a networked federation of objects, and in many cases one is dealing with
`proxy objects, not the real objects.
`This is the same with remote events, except that in this case we often have the
`direction of proxies reversed. To see what I mean by this, consider what happens if
`a client wants to monitor any changes in the service. The client will already have a
`proxy object for the service, and it will use this proxy to register itself as a listener.
`However, the service proxy will most likely just hand this listener back off to the
`service itself (that is what proxies, such as RMI proxies, do). So we need to get a
`
`proxy for the client over to the service.
`
`266
`
`

`
`Remote Events
`
`Consider the file classification problems we looked at in earlier chapters. The
`file classifier had a hard—coded set of filename extensions built in. However, it
`
`would be possible to extend these, if applications come along that know how to
`
`define (and maybe handle) such extensions. For example, an application would
`locate the file classification server, and using an exported method from the file
`classification interface would add the new MIME type and file extension. This is
`
`no departure from any standard Java or earlier Iini stuff. It only affects the imple-
`mentation level of the file classifier, changing it from a static list of filename
`extensions to a more dynamic one.
`
`What it does affect is the poor application that has been blocked (and is prob—
`ably sleeping) on an unknown filename extension.When the classifier installs a
`
`new file type, it can send an event saying so. The blocked application could then
`try again to see if the extension is now known. If so, it uses it, and if not, it blocks
`
`again. Note that we don’t bother with identifying the actual state change, since it is
`just as easy to make another query once you know that the state has changed.
`More complex situations may require more information to be maintained. How-
`
`ever, in order to get to this situation, the application must have registered its
`interest in events, and the event producer must be able to find the listener.
`
`How this gets resolved is for the client to first find the service in the same way
`as we discussed in Chapter 6. The client ends up with a proxy object for the service
`
`in the client's address space. One of the methods on the proxy will add an event lis-
`tener, and this method will be called by the client.
`For simplicity, assume that the client is being added as a listener to the service.
`
`The client will call the add listener method of the proxy, with the client as parame—
`ter. The proxy will then call the real object's add listener method, back on its server
`side. But in doing this, we have made a remote call across the network, and the cli~
`
`ent, which was local to the call on the proxy, is now remote to the real object, so
`what the real object is getting is a proxy to the client. When the service makes noti-
`fication calls to the proxy listeners, the client’s proxy can make a remote call back
`
`to the client itself. These proxies are shown in Figure 14-3.
`
`Client
`
`Service
`
`proxy
`
`Service
`
`Figure 14—3. Proxies for services and listeners
`
`267
`
`

`
`Chapter 1 4
`
`File Classifier with Events
`
`Let’s make this discussion more concrete by looking at a new file classifier applica-
`
`tion that can have its set of mappings dynamically updated.
`The first thing to be modified is the FileC1assifier interface. This needs to be
`extended to a MutableFileClassifier interface, known to all objects. This new
`
`interface adds methods that will add and remove types, and that will also register
`listeners for events. The event types are labeled with two constants. The listener
`model is simple, and does not include handbacks or leases. The sequence identifier
`must be increasing, so we just add 1 on each event generation, although we don’t
`really need it here: it is easy for a listener to just make MIME type queries again.
`
`package common;
`
`import java.io.Serializable;
`
`/*>i<
`
`* MutableFi1eClassifier .java
`*/
`
`import net . j ini . core . event . RemoteEventListener;
`import net.jini.coIe.event.EventRegistration;
`
`public interface MutableFileClassifier extends FileClassifier {
`
`static final public long ADD_TYPE = 1;
`static final public long REMOVE_TYPE = 2;
`
`/*
`
`* Add the MIME type for the given suffix.
`* The suffix does not contain '.' e.g. "gif".
`* Overrides any previous MIME type for that suffix
`*/
`
`public void addType(String suffix, MIMEType type)
`throws java.rmi.RemoteException;
`
`/*
`
`* Delete the MIME type for the given suffix.
`* The suffix does not contain '.' e.g. "gif".
`* Does nothing if the suffix is not known
`*/
`
`public void removeMIMEType(String suffix, MIMEType type)
`throws java.rmi.RemoteException;
`
`268
`
`

`
`Remote E1/en ts
`
`public EventRegistration addRemoteListener(RemoteEventListener listener)
`throws java.rmi.RemoteException;
`
`} // MutableFi1eClasssifier
`
`The RemoteFi1eC1ass:'n‘ie:r interface is known only to services, and it just
`changes its package and inheritance for any service implementation:
`
`package mutable;
`
`import common.MutableFileClassifier;
`import
`java.Imi.Remote;
`
`/*>l<
`
`* RemoteFi1eC1assifier.java
`*/
`
`public interface RemoteFi1eClassifieI extends MutableFileClassifier, Remote {
`
`} // RemoteFileC1asssifier
`
`Previous implementations of file classifier services (such as in Chapter 8) use a
`static list of if. . .then statements because they deal with a fixed set of types. For
`this implementation, where the set of mappings can change, we change the imple-
`
`mentation to a dynamic map keyed on file suffixes. It manages the event listener
`
`list for multiple listeners in the simple way discussed earlier in this chapter, and it
`
`generates events whenever a new suffix/ type is added or successfully removed.
`The following code is an implementation of the file classifier service with this
`alternative implementation and an event list:
`
`package mutable;
`
`import java.rmi.server.UnicastRemoteObject;
`import java.Imi.MarshalledObject;
`import net.jini.coIe.event.RemoteEventListener;
`
`import net.jini.core.event.RemoteEvent;
`import net.jini.core.event.EventRegistration;
`
`import java.rmi.RemoteException;
`import net.jini.core.event.UnknownEventException ;
`
`import javax.swing.event.EventListenerList;
`
`import common.MIMEType;
`
`269
`
`

`
`Chapmr14
`
`import common.MutableFileClassifier;
`import java.util.Map;
`import java.util.HashMap;
`
`/>i<*
`
`* FileClassifierImpl.java
`*/
`
`public class FileClassifieIImp1 extends Unicastkemoteobject
`implements RemoteFileClassifier {
`
`/**
`
`* Map of String extensions to MIME types
`*/
`
`protected Map map = new HashMap();
`
`/*>k
`
`* Listeners for change events
`*/
`
`protected EventListenerList listenerList = new EventListenerList();
`
`protected long seqNum = 0L;
`
`public MIMEType getMIMEType(String fileName)
`throws java.rmi.RemoteException {
`System.out.println(”Called with " + fileName);
`
`MIMEType type;
`String fileExtension;
`int dotIndex = fileName.lastIndexOf(‘.‘);
`
`if (dotIndex == -1 || dotIndex + 1 == fileName.length()) {
`// can't find suitable suffix
`
`return null;
`
`fileExtension= fileName.substring(dotIndex + 1);
`
`type = (MIMEType) map.get(fileExtension);
`return type;
`
`public void addType(String suffix, MIMEType type)
`throws java.rmi.RemoteException {
`
`270
`
`

`
`Remote Events
`
`type);
`map.put(suffix;
`fireNotify(ADD_TYPE);
`
`} p
`
`ublic void removeMIMEType(String suffix, MIMEType type)
`throws java.rmi.RemoteException {
`
`!= null) {
`if (map.remove(suffix)
`fireNotify(REMOVE_TYPE);
`
`} p
`
`ublic Eventkegistration addRemoteListener(RemoteEventListener listener)
`throws java.rmi.RemoteException {
`listenerList.add(RemoteEventListener.class, listener);
`
`return new EventRegistration(0, this, null, 0);
`
`// Notify all listeners that have registered interest for
`// notification on this event type.
`The event instance
`
`// is lazily created using the parameters passed into
`// the fire method.
`
`protected void fireNotify(long eventID)
`RemoteEvent remoteEvent = null;
`
`{
`
`// Guaranteed to return a non—null array
`
`0bject[] listeners = listenerList.getListenerList();
`
`// Process the listeners last to first, notifying
`// those that are interested in this event
`
`for (int i = listeners.length - 2; i >= 0; i -= 2) {
`if (listeners[i] == RemoteEventListener.class) {
`RemoteEventListener listener = (RemoteEventListener) listeners[i+1];
`if (remoteEvent == null) {
`remoteEvent = new RemoteEvent(this, eventID,
`seqNum++, null);
`
`{
`
`} t
`
`ry {
`listener.notify(remoteEvent);
`} catch(UnknownEventException e)
`
`e.printStackTrace();
`} catch(RemoteException e)
`e.printStackTrace();
`
`{
`
`271
`
`

`
`} p
`
`throws java.rmi.RemoteException {
`ublic FileClassifierImpl()
`// load a predefined set of MIME type mappings
`
`map.put(”gif", new MIMEType("image“, "gif”));
`map.put("jpeg", new MIMEType("image", ”jpeg"));
`map.put(”mpg", new MIMEType("video", "mpeg"));
`map.put("txt", new MIMEType(”text", "plain"));
`map.put(”html", new MIMEType(“text", "html"));
`
`}
`} // FileClassifierImpl
`
`The proxy changes its inheritance, and as a result has more methods to imple-
`ment, which it just delegates to its server object. The following class is for the proxy:
`
`package mutable;
`
`im

This document is available on Docket Alarm but you must sign up to view it.


Or .

Accessing this document will incur an additional charge of $.

After purchase, you can access this document again without charge.

Accept $ Charge
throbber

Still Working On It

This document is taking longer than usual to download. This can happen if we need to contact the court directly to obtain the document and their servers are running slowly.

Give it another minute or two to complete, and then try the refresh button.

throbber

A few More Minutes ... Still Working

It can take up to 5 minutes for us to download a document if the court servers are running slowly.

Thank you for your continued patience.

This document could not be displayed.

We could not find this document within its docket. Please go back to the docket page and check the link. If that does not work, go back to the docket and refresh it to pull the newest information.

Your account does not support viewing this document.

You need a Paid Account to view this document. Click here to change your account type.

Your account does not support viewing this document.

Set your membership status to view this document.

With a Docket Alarm membership, you'll get a whole lot more, including:

  • Up-to-date information for this case.
  • Email alerts whenever there is an update.
  • Full text search for other cases.
  • Get email alerts whenever a new case matches your search.

Become a Member

One Moment Please

The filing “” is large (MB) and is being downloaded.

Please refresh this page in a few minutes to see if the filing has been downloaded. The filing will also be emailed to you when the download completes.

Your document is on its way!

If you do not receive the document in five minutes, contact support at support@docketalarm.com.

Sealed Document

We are unable to display this document, it may be under a court ordered seal.

If you have proper credentials to access the file, you may proceed directly to the court's system using your government issued username and password.


Access Government Site

We are redirecting you
to a mobile optimized page.





Document Unreadable or Corrupt

Refresh this Document
Go to the Docket

We are unable to display this document.

Refresh this Document
Go to the Docket