|
Granting and Handling LeasesThe preceding discussion looked at leases from the side of the client that receives a lease and has to manage it. The converse of this is the agent that grants leases and has to manage things from its side. This is more advanced material that you can skip for now if you want ”it is not really needed until Chapter 13. A lease can be granted for almost any remote service ”any one where one object wants to maintain information about another one that is not within the same virtual machine. As with other remote services, there are the added partial failure modes, such as network crash, remote service crash, timeouts, and so on. An object that keeps information on a remote service will hand out a lease to the service and will want the remote service to keep "pinging" it periodically to say that it is still alive and that it wants the information kept. Without this periodic assurance, the object might conclude that the remote service has vanished or is somehow unreachable, and that it should discard the information about it. Leases are a very general mechanism for allowing one service to have confidence in the existence of the other for a limited period. Because they are general, they allow for a great deal of flexibility in use. Because of the potential variety of services, some parts of the Jini lease mechanism cannot be completely defined and must be left as interfaces for applications to fill in. This generality means that all of the details are not filled in for you, as your own requirements cannot be completely predicted in advance. A lease is given as an interface, and any agent that wishes to grant leases must implement this interface. Jini provides two implementations , an AbstractLease and a subclass of this, a LandlordLease . A main issue in implementing a particular lease class lies in setting a policy for handling the initial request for a lease period and in deciding what to do when a renewal request comes in. A couple of simple possibilities are these:
Of course, there are many more possibilities based on the lessor's expected time to live, system load, etc. There are other issues, though. Any particular lease will need a time-out mechanism. Also, a group of leases can be managed together, and this can reduce the amount of overhead involved in managing individual leases. Abstract LeaseAn abstract lease gives a basic implementation of a lease that can almost be used for simple leases. package com.sun.jini.lease; public abstract class AbstractLease implements Lease, java.io.Serializable { protected AbstractLease(long expiration); public long getExpiration(); public int getSerialFormat(); public void setSerialFormat(int format); public void renew(long duration); protected abstract long doRenew(long duration); }
This class supplies straightforward implementations of much of the Lease interface, with three provisos:
Thus, this class implements the easy things, and leaves all matters of policy to concrete subclasses. Landlord Lease PackageThe landlord is a package that allows more complex leasing systems to be built. It is not part of the Jini specification, but is supplied as a set of classes and interfaces. The set is not complete in itself ”some parts are left as interfaces and need to have class implementations. These will be supplied by a particular application. A landlord looks after a set of leases. Leases are identified to the landlord by a cookie , which is just some object that uniquely labels each lease to its landlord. It could be an Integer , for example, with a new value for each lease. A landlord does not need to create leases itself, as it can use a landlord lease factory to do this. (But, of course, it can create them, depending on how an implementation is done.) When a client wishes to cancel or renew a lease, it asks the lease to perform the renewal, and in turn the lease asks its landlord to do it. A client is unlikely to ask the landlord directly, as it will only have been given a lease, not a landlord. The principal classes and interfaces in the landlord package are shown in Figure 7-2, where the interfaces are shown in italicized font and the classes in normal font. Figure 7-2: Class diagram of the landlord package This fairly complex set of classes and interfaces is driven by a number of factors:
Java uses interfaces as specifications without implementation details. For individual classes this is often fine. However, using interfaces can be limiting when you are dealing with a set of classes that are expected to interact in certain ways. Interfaces do not show the interactions that may need to exist in order for an implementation of the set of classes to function together. This means the interface definitions are not complete as they stand, because they fail to show the links between classes that must exist in any implementation. To see what these links actually are, let us look at a simple implementation for the Foo landlord package. If we have a landlord for a Foo resource, then we could end up with the class structure shown in Figure 7-3. Figure 7-3: Class diagram of a landlord implementation This diagram uses a UML class diagram annotated with arrows and multiplicities. An association with an arrow means that the object at the source of the arrow will know about the object at the other end of the arrow. For example, each LandlordLease knows about (has a reference to) a FooLandlord , but the landlord does not know about any leases. At each end of each association between classes, the multiplicity of that end of the link is also shown. A "*" is a wildcard pattern, meaning "zero to many." So for example, any number of LandlordLeases (from zero upwards) may know about a single FooLandlord . Some comments are appropriate about the directions and multiplicities:
LandlordLease ClassThe LandlordLease class extends AbstractLease . This class has the private fields cookie and landlord , as shown in Figure 7-4. Figure 7-4: The class diagram for LandlordLease Implementation of the methods cancel() and doRenew() in the LandlordLease is deferred to its landlord. The implementation of these methods in LandlordLease simply passes the requests on to the landlord: public void cancel() { landlord.cancel(cookie); } protected long doRenew(long renewDuration) { return landlord.renew(cookie, renewDuration); } The LandlordLease class can be used as is, with no subclassing needed. Note that the landlord system produces these leases but does not actually keep them anywhere ”they are passed on to clients , which then use the lease to call the landlord and hence interact with the landlord lease system. Within the landlord system, on the lessor side, the cookie is used as an identifier for the lease. LeasedResource InterfaceA LeasedResource is a convenience wrapper around a resource that includes extra information about a lease and methods for use by landlords. It defines an interface as follows : public interface LeasedResource { public void setExpiration(long newExpiration); public long getExpiration(); public Object getCookie(); } This interface includes the cookie , a unique identifier for a lease within a landlord system, as well as expiration information for the lease. This is all the information maintained about the lease that has been given out to a client. An implementation of LeasedResource will typically include the resource that is leased, plus a method of setting the cookie. The following code shows an example: /** * FooLeasedResource.java */ package foolandlord; import com.sun.jini.lease.landlord.LeasedResource; public class FooLeasedResource implements LeasedResource { static protected int cookie = 0; protected int thisCookie; protected Foo foo; protected long expiration = 0; public FooLeasedResource(Foo foo) { this.foo = foo; thisCookie = cookie++; } public void setExpiration(long newExpiration) { this.expiration = newExpiration; } public long getExpiration() { return expiration; } public Object getCookie() { return new Integer(thisCookie); } public Foo getFoo() { return foo; } } // FooLeasedResource LeasePolicy InterfaceA lease policy is used when a lease is first granted and when it tries to renew itself. The time requested may be granted, modified, or denied . A lease policy is specified by the LeasePolicy interface. package com.sun.jini.lease.landlord; public interface LeasePolicy { public Lease leaseFor(LeasedResource resource, long requestedDuration) throws LeaseDeniedException; public long renew(LeasedResource resource, long requestedDuration) throws LeaseDeniedException, UnknownLeaseException; public boolean ensureCurrent(LeasedResource resource); } This interface includes a factory method, leaseFor() , that returns a lease based on the policy and request. LeaseDurationPolicy ClassAn implementation of the LeasePolicy interface is given by LeaseDurationPolicy class. This class grants and renews leases based on constant values for maximum and default lease durations, as shown here: package com.sun.jini.lease.landlord; public class LeaseDurationPolicy implements LeasePolicy { public LeaseDurationPolicy(long maximum, long defaultLength, Landlord landlord, LeaseManager mgr, LandlordLeaseFactory factory); public Lease leaseFor(LeasedResource resource, long requestedDuration) throws LeaseDeniedException; public long renew(LeasedResource resource, long requestedDuration); public boolean ensureCurrent(LeasedResource resource); } In addition to implementing the interface methods, the constructor also passes in the factory to be used (which will probably be a LandlordLease.Factory ) and maximum and default lengths for leases. The maximum duration is to set a hard upper limit (which could be, say, Lease.FOREVER ), while the default is what is granted if the client asks for a duration of Lease.ANY . LeaseManager InterfaceThe operations that can be carried out on a lease are creation, renewal, and cancellation. The first two are subject to the lease policy and must be handled by the leaseFor() and renew() methods of the policy. These set or alter the properties of a single lease. There may be many leases for a resource, or even many resources with one or more leases. Some level of management for a group of leases may be needed, and this is done by a LeaseManager . The LeaseManager interface is defined as follows: package com.sun.jini.lease.landlord; public interface LeaseManager { public void register(LeasedResource resource, long duration); public void renewed(LeasedResource resource, long duration, long oldExpiration); } This LeaseManager doesn't actually manage the leases, since they have been given to the client. Rather, it handles the lease resource, which has the cookie identifier and the expiration time for the lease. An implementation of LeaseManager will look after a set of leases (really, their resources) by adding a new lease resource to its set for each lease, and by updating information about renewals. The interface does not include a method for informing the manager of cancelled leases, though ”that is done to the Landlord instead, by the lease when the lease's cancel() method is called. This split responsibility between LeaseManager and Landlord is a little awkward and can possibly lead to memory leaks, with the manager holding a reference to a lease (resource) that the landlord has cancelled. Either the list of lease resources must be shared between the two, or the landlord must ensure that it passes on cancellations to the manager. There is also the question of how the lease manager is informed of changes to individual leases by the lease policy. The LeaseDurationPolicy will pass on this information in its leaseFor() and renew() methods, but other implementations of LeasePolicy need not. As we only use the LeasePolicy implementation, we are okay here. A third question is who looks after leases expiring, and how this can be done. No part of the landlord specifications talk about this or give a suitable method. This suggests that it, too, is subject to some sort of policy, but it is not one with landlord support. It is left to implementations of one of the landlord interfaces, or to a subclass. A convenient place to locate this checking is in the lease manager, because it has knowledge of all the leases and their duration. Possible ways of doing this include the following:
The FooLeaseManager implements this third polling mechanism method: /** * FooLeaseManager.java */ package foolandlord; import java.util.*; import net.jini.core.lease.Lease; import com.sun.jini.lease.landlord.LeaseManager; import com.sun.jini.lease.landlord.LeasedResource; import com.sun.jini.lease.landlord.LeaseDurationPolicy; import com.sun.jini.lease.landlord.Landlord; import com.sun.jini.lease.landlord.LandlordLease; import com.sun.jini.lease.landlord.LeasePolicy; public class FooLeaseManager implements LeaseManager { protected static long DEFAULT_TIME = 30*1000L; protected Vector fooResources = new Vector(); protected LeaseDurationPolicy policy; public FooLeaseManager(Landlord landlord) { policy = new LeaseDurationPolicy(Lease.FOREVER, DEFAULT_TIME, landlord, this, new LandlordLease.Factory()); new LeaseReaper().run(); } public void register(LeasedResource r,long duration) { fooResources.add(r); } public void renewed(LeasedResource r, long duration, long olddur) { // no smarts in the scheduling, so do nothing } public void cancelAll(Object[] cookies) { for (int n = cookies.length; --n >= 0;) { cancel(cookies[n]); } } public void cancel(Object cookie) { for (int n = fooResources.size(); --n >= 0;) { FooLeasedResource r = (FooLeasedResource) fooResources.elementAt(n); if (r.getCookie().equals(cookie)) { fooResources.removeElementAt(n); } } } public LeasePolicy getPolicy() { return policy; } public LeasedResource getResource(Object cookie) { for (int n = fooResources.size(); --n >= 0;) { FooLeasedResource r = (FooLeasedResource) fooResources.elementAt(n); if (r.getCookie().equals(cookie)) { return r; } } return null; } class LeaseReaper extends Thread { public void run() { while (true) { try { Thread.sleep(DEFAULT_TIME) ; } catch (InterruptedException e) { } for (int n = fooResources.size()1; n >= 0; n--) { FooLeasedResource r = (FooLeasedResource) fooResources.elementAt(n) ; if (!policy.ensureCurrent(r)) { System.out.println("Lease expired for cookie = " + r.getCookie()) ; fooResources.removeElementAt(n); } } } } } } // FooLeaseManager Landlord InterfaceThe Landlord is the final interface in the package that we need for a basic landlord system. Other classes and interfaces, such as LeaseMap are for handling leases in batches, and will not be dealt with here. The Landlord interface is as follows: package com.sun.jini.lease.landlord; public interface Landlord extends Remote { public long renew(Object cookie, long extension) throws LeaseDeniedException, UnknownLeaseException, RemoteException; public void cancel(Object cookie) throws UnknownLeaseException, RemoteException; public RenewResults renewAll(Object[] cookie, long[] extension) throws RemoteException; public void cancelAll(Object[] cookie) throws LeaseMapException, RemoteException; } The renew() and cancel() methods are usually called from the renew() and cancel() methods of a particular lease. The renew() method needs to use a policy object to ask for renewal, and in the FooLandlord implementation, it gets this policy from the FooLeaseManager . The cancel() method needs to modify the list of leases, and in the FooLandlord implementation, it passes this on to the FooLeaseManager , since that is the only object that maintains a list of resources. There must be a method to ask for a new lease for a resource, and this is not specified by the landlord package. This request will probably be made on the lease-granting side, and this should have access to the landlord object, which forms a central point for lease management. So, an implementation of this interface will quite likely have a method such as public Lease newFooLease(Foo foo, long duration); which will give a lease for a resource. The lease used in the landlord package is a LandlordLease . This contains a private field, which is a reference to the landlord itself. The lease is given to a client as a result of newFooLease() , and this client will usually be a remote object. This will involve serializing the lease and sending it to this remote client. While serializing it, the landlord field will also be serialized and sent to the client. When the client methods such as renew() are called, the implementation of the LandlordLease will make a call to the landlord. The lease is on the client, which by then will be remote from its origin where the landlord lives. That means the landlord object invoked by the lease will need to be a remote object making a remote call. The Landlord interface already extends Remote , but if it is to run as a remote object, then the easiest way is for FooLandlord to extend the UnicastRemoteObject class. Putting all this together for the FooLandlord class gives us this: /** * FooLandlord.java */ package foolandlord; import com.sun.jini.lease.landlord.*; import net.jini.core.lease.LeaseDeniedException; import net.jini.core.lease.Lease; import java.rmi.server.UnicastRemoteObject; import java.rmi.Remote; public class FooLandlord extends UnicastRemoteObject implements Landlord { FooLeaseManager manager; public FooLandlord() throws java.rmi.RemoteException { manager = new FooLeaseManager(this); } public void cancel(Object 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); } return 1; } public Lease newFooLease(Foo foo, long duration) throws LeaseDeniedException { FooLeasedResource r = new FooLeasedResource(foo); return manager.getPolicy().leaseFor(r, duration); } public Landlord.RenewResults renewAll(java.lang.Object[] cookies, long[] extensions) { long[] granted = new long[cookies.length]; Exception[] denied = new Exception[cookies.length]; for (int n = cookies.length; --n >= 0;) { try { granted[n] = renew(cookies[n], extensions[n]); denied[n] = null; } catch(Exception e) { granted[n] = 1; denied[n] = e; } } return new Landlord.RenewResults(granted, denied); } } // FooLandlord Building an implementation of the landlord package, such as the Foo package, means providing implementations of the Landlord , LeasedResource , and LeaseManager interfaces. This has been done using the FooLandlord , FooLeasedResource , and FooLeaseManager classes. |