previous chapter table of contents next chapter


Activatable objects are an example of services that are not continuously alive. Mobile services, such as those that will exist on mobile phones, are another. These services will be brought to life on demand (as activatable objects), or will join the network on occasion. These services raise a number of problems, and one was skirted around in the last section: How do you renew leases when the object is not alive ?

Activatable objects are brought back to life when methods are invoked on them, and the expiration of a lease does not cause any methods to be invoked. There is no "lease-expiring event" generated that could cause a listener method to be invoked, either. It is true that a ServiceRegistrar such as reggie will generate an event when a lease changes status, but this is a "service removed" event rather than a "service about to be removed" event ”at that point it is too late.

If a server is alive, then it can use a LeaseRenewalManager to keep leases alive, but there are two problems with this: first the renewal manager works by sleeping and waking up just in time to renew the leases, and second, if the server exits, then no LeaseRenewalManager will continue to run.

Jini 1.1 supplies a lease renewal service that partly solves these problems. Since it runs as a service, it has an independent existence that does not depend on the server for any other service. It can act like a LeaseRenewalManager in keeping track of leases registered with it, renewing them as needed. In general, it can keep leases alive without waking the service itself, which can slumber until it is activated by clients calling methods.

There is a small hiccup in this system, though: how long should the LeaseRenewalService keep renewing leases for a service? The LeaseRenewalManager utility has a simple solution: keep renewing while the server for that service is alive. If the server dies, taking down a service, then it will also take down the LeaseRenewalManager running in the same JVM, so leases will expire, as expected, after an interval.

But this mechanism won't work for LeaseRenewalService because the managed service can disappear without the LeaseRenewalService knowing about it. So the lease renewal must be done on a leased basis itself! The LeaseRenewalService will renew leases for a service only for a particular amount of time, as specified by a lease. The service will still have to renew its lease, but with a LeaseRenewalService instead of a bunch of lookup services. The lease granted by this service will need to be of much longer duration than those granted by the lookup services for this to be of value.

Activatable services can only be woken by calling one of their methods. The LeaseRenewalService accomplishes this by generating renewal events in advance and calling a notify() method on a listener. If the listener is the activatable object, this will wake it up so that it can perform the renewal. If the rmid process managing the service has died or is unavailable, then the event will not be delivered and the LeaseRenewalService can remove this service from its renewal list.

This is not quite satisfactory for other types of " dormant " services, such as might exist on mobile phones, since there is no equivalent of rmid to handle activation. Instead, the mobile phone service might say that it will connect once a day and renew the lease, as long as the LeaseRenewalService agrees to keep the lease for at least a day. This is still "negotiable," in that the service asks for a duration and the LeaseRenewalService replies with a value that might not be so long. Still, it should be better than dealing with the lookup services.

The Norm Service

Jini 1.1 supplies an implementation of LeaseRenewalService called norm . This is a non-lazy Activatable service that requires rmid to be running. This is run with the following command

 java -jar [setup_jvm_options] executable_jar_file           codebase_arg norm_policy_file_arg           log_directory_arg           [groups] [server_jvm] [server_jvm_args] 

as in the following

 java -jar \         -Djava.security.policy=/files/jini1_1/example/txn/policy.all \         /files/jini1_1/lib/norm.jar \         http://`hostname`:8080/norm-dl.jar \         /files/jini1_1/example/books/policy.all /tmp/norm_log 

The first security file defines the policy that will be used for the server startup. The norm.jar file contains the class files for the norm service. This exports RMI stubs, and the class definitions for these are in norm-dl.jar . The second security file defines the policy file that will be used in the execution of the LeaseRenewalService methods. Finally, the log file is used to keep state, so that it can keep track of the leases it is managing.

The norm service will maintain a set of leases for a period of up to two hours. The reggie lookup service only grants leases for five minutes, so using this service increases the amount of time between renewing leases by a factor of over 20.

Using the LeaseRenewalService

The norm service exports an object of type LeaseRenewalService , which is defined by the following interface:

 package net.jini.lease; public interface LeaseRenewalService {     LeaseRenewalSet createLeaseRenewalSet(long leaseDuration)         throws java.rmi.RemoteException; } 

A server that wants to use the lease renewal service will first find this service and then call the createLeaseRenewal() method. The server requests a leaseDuration value, measured in milliseconds , for the lease service to manage a set of leases. The lease service creates a lease for this request, but the lease time may be less than the requested time (for norm , it is a maximum of two hours). In order for the lease service to continue to manage the set beyond the lease's expiry, the lease must be renewed before expiration. Since the service may be inactive at the time of expiry, the LeaseRenewalSet can be asked to register a listener object that will receive an event containing the lease. This will activate a dormant listener so that the listener can renew the lease in time. If the lease for the LeaseRenewalSet is allowed to lapse, then eventually all the leases for the services it was managing will also expire, making the services unavailable.

The LeaseRenewalSet returned from createLeaseRenewalSet has an interface including the following:

 package net.jini.lease; public interface LeaseRenewalSet {     public void renewFor(Lease leaseToRenew,                          long membershipDuration)                 throws RemoteException;     public EventRegistration setExpirationWarningListener(RemoteEventListener listener,                          long minWarning,                          MarshalledObject handback)                 throws RemoteException;     .... } 

The renewFor() method adds a new lease to the set being looked after. The LeaseRenewalSet will keep renewing the lease until either the requested membershipDuration expires or the lease for the whole LeaseRenewalSet expires (or until an exception happens, like a lease being refused ).

Setting an expiration warning listener means that the notify () method of the listener will be called at least minWarning milliseconds before the lease for the set expires. The event argument will actually be an ExpirationWarningEvent :

 package net.jini.lease; public class ExpirationWarningEvent extends RemoteEvent {     Lease getLease(); } 

This allows the listener to get the lease for the LeaseRenewalSet and (probably) renew it. Here is a simple activatable class that can renew the lease:

 package activation; import java.rmi.activation.Activatable; import java.rmi.activation.ActivationID; import java.rmi.MarshalledObject; import net.jini.core.event.RemoteEvent; import net.jini.core.event.RemoteEventListener; import net.jini.core.lease.Lease; import net.jini.lease.ExpirationWarningEvent; public class RenewLease extends Activatable     implements RemoteEventListener {     public RenewLease(ActivationID id, MarshalledObject data)         throws java.rmi.RemoteException {         super(id, 0);     }     public void notify(RemoteEvent evt) {         System.out.println("expiring... " + evt.toString());         ExpirationWarningEvent eevt = (ExpirationWarningEvent) evt;         Lease lease = eevt.getLease();         try {             // This is short, for testing. Try 2+ hours             lease.renew(20000L);         } catch(Exception e) {             e.printStackTrace();         }          System.out.println("Lease renewed for " +                            (lease.getExpiration()                              System.currentTimeMillis()));     } } 

The server will need to register the service and export it as an activatable object. This is done in exactly the same way as in the FileClassifierServer example of the first section of this chapter. In addition, it will need to do a few other things:

  • It will need to register the lease listener with the activation system as an activatable object.
  • It will need to find a LeaseRenewalService from a lookup service.
  • It will need to register all leases from lookup services with the LeaseRenewalService . Since it may find lookup services before it finds the renewal service, it will need to keep a list of lookup services found before finding the service, in order to register them with it.

Adding these additional requirements to the FileClassifierServer of the first section results in this server:

 package activation; import rmi.RemoteFileClassifier; import net.jini.discovery.LookupDiscovery; import net.jini.discovery.DiscoveryListener; import net.jini.discovery.DiscoveryEvent; import net.jini.core.lookup.ServiceRegistrar; import net.jini.core.lookup.ServiceItem; import net.jini.core.lookup.ServiceRegistration; import net.jini.core.lookup.ServiceTemplate; import net.jini.core.event.RemoteEvent; import net.jini.core.event.RemoteEventListener; import net.jini.core.lease.Lease; import net.jini.lease.LeaseRenewalService; import net.jini.lease.LeaseRenewalSet; import java.rmi.RMISecurityManager; import java.rmi.MarshalledObject; import java.rmi.activation.ActivationDesc; import java.rmi.activation.ActivationGroupDesc; import java.rmi.activation.ActivationGroupDesc.CommandEnvironment; import java.rmi.activation.Activatable; import java.rmi.activation.ActivationGroup; import java.rmi.activation.ActivationGroupID; import java.rmi.activation.ActivationID; import java.rmi.MarshalledObject; import java.util.Properties; import java.util.Vector; import java.rmi.activation.UnknownGroupException; import java.rmi.activation.ActivationException; import java.rmi.RemoteException; /**  * FileClassifierServerLease.java  */ public class FileClassifierServerLease     implements DiscoveryListener {     static final protected String SECURITY_POLICY_FILE =         "/home/jan/projects/jini/doc/policy.all";     // Don't forget the trailing '/'!     static final protected String CODEBASE = "http://localhost/classes/";     protected RemoteFileClassifier stub;     protected RemoteEventListener leaseStub;     // Lease renewal management     protected LeaseRenewalSet leaseRenewalSet = null;     // List of leases not yet managed by a LeaseRenewalService     protected Vector leases = new Vector();     public static void main(String argv[]) {         new FileClassifierServerLease(argv);         // stick around while lookup services are found         try {             Thread.sleep(10000L);         } catch(InterruptedException e) {             // do nothing         }         // the server doesn't need to exist anymore         System.exit(0);     }     public FileClassifierServerLease(String[] argv) {         // install suitable security manager         System.setSecurityManager(new RMISecurityManager());         // Install an activation group         Properties props = new Properties();         props.put("java.security.policy",                 SECURITY_POLICY_FILE);         ActivationGroupDesc.CommandEnvironment ace = null;         ActivationGroupDesc group = new ActivationGroupDesc(props, ace);         ActivationGroupID groupID = null;         try {             groupID = ActivationGroup.getSystem().registerGroup(group);         } catch(RemoteException e) {             e.printStackTrace();             System.exit(1);         } catch(ActivationException e) {             e.printStackTrace();             System.exit(1);         }         try {             ActivationGroup.createGroup(groupID, group, 0);         } catch(ActivationException e) {             e.printStackTrace();             System.exit(1);         }         String codebase = CODEBASE;         MarshalledObject data = null;         ActivationDesc desc = null;         ActivationDesc descLease = null;         try {             desc = new ActivationDesc("activation.FileClassifierImpl",                                                  codebase, data);             descLease = new ActivationDesc("activation.RenewLease",                                                  codebase, data);         } catch(ActivationException e) {             e.printStackTrace();             System.exit(1);         }         try {             stub = (RemoteFileClassifier) Activatable.register(desc);             leaseStub = (RemoteEventListener) Activatable.register(descLease);         } catch(UnknownGroupException e) {             e.printStackTrace();             System.exit(1);         } catch(ActivationException e) {             e.printStackTrace();             System.exit(1);         } catch(RemoteException e) {             e.printStackTrace();             System.exit(1);         }         LookupDiscovery discover = null;         try {             discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);         } catch(Exception e) {             System.err.println(e.toString());             System.exit(1);         }         discover.addDiscoveryListener(this);     }     public void discovered(DiscoveryEvent evt) {         ServiceRegistrar[] registrars = evt.getRegistrars();         RemoteFileClassifier service;         for (int n = 0; n < registrars.length; n++) {             ServiceRegistrar registrar = registrars[n];             // export the proxy service             ServiceItem item = new ServiceItem(null,                                                stub,                                                null);             ServiceRegistration reg = null;             try {             reg = registrar.register(item, Lease.FOREVER);         } catch(java.rmi.RemoteException e) {             System.err.print("Register exception: ");             e.printStackTrace();             // System.exit(2);             continue;         }         try {             System.out.println("service registered at " +                                registrar.getLocator().getHost());         } catch(Exception e) {         }         Lease lease = reg.getLease();         // if we have a lease renewal manager, use it         if (leaseRenewalSet != null) {             try {                 leaseRenewalSet.renewFor(lease, Lease.FOREVER);             } catch(RemoteException e) {                 e.printStackTrace();             }         } else {             // add to the list of unmanaged leases             leases.add(lease);             // see if this lookup service has a lease renewal manager             findLeaseService(registrar);         }     } } public void findLeaseService(ServiceRegistrar registrar) {     System.out.println("Trying to find a lease service");     Class[] classes = {LeaseRenewalService.class};     ServiceTemplate template = new ServiceTemplate(null, classes,                                                null);     LeaseRenewalService leaseService = null;     try {         leaseService = (LeaseRenewalService) registrar.lookup(template);     } catch(RemoteException e) {         e.printStackTrace();         return;     }     if (leaseService == null) {         System.out.println("No lease service found");            return;        }        try {            // This time is unrealistically small - try 10000000L            leaseRenewalSet = leaseService.createLeaseRenewalSet(20000);            System.out.println("Found a lease service");            // register a timeout listener            leaseRenewalSet.setExpirationWarningListener(leaseStub, 5000,                                                     null);            // manage all the leases found so far            for (int n = 0; n < leases.size(); n++) {                Lease ll = (Lease) leases.elementAt(n);                leaseRenewalSet.renewFor(ll, Lease.FOREVER);            }            leases = null;        } catch(RemoteException e) {            e.printStackTrace();        }        Lease renewalLease = leaseRenewalSet.getRenewalSetLease();        System.out.println("Lease expires in " +                           (renewalLease.getExpiration()                             System.currentTimeMillis()));     }     public void discarded(DiscoveryEvent evt) {     } } // FileClassifierServerLease 

A Programmer[ap]s Guide to Jini Technology
A Programmer[ap]s Guide to Jini Technology
ISBN: 1893115801
Year: 2000
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net