Chapter 7: Leasing

Leasing is the mechanism used between applications to give access to resources over a period of time in an agreed manner

7.1. Leases

Leases are requested for a period of time. In distributed applications, there may be partial failures of the network or of components on this network. Leasing is a way for components to register that they are alive, but for them to be ``timed out'' if they have failed, are unreachable, etc. In Jini, one use of leasing is for a service to request that a copy be kept on a lookup service for a certain length of time for delivery to clients on request. The service requests a time in the ServiceRegistrar's method register(). Two special values of the time are

  1. Lease.ANY - the service lets the lookup service decide on the time
  2. Lease.FOREVER - the request is for a lease that never expires

The lookup service acts as the granter of the lease, and decides how long it will actually create the lease for. (The lookup service from Sun typically sets the lease time as only five minutes.) Once it has done that, it will attempt to ensure that the request is honoured for that period of time. The lease is returned to the service, and is accessible through the method getLease() of the ServiceRegistration object. These objects are shown in figure 7.1

Figure 7.1: Objects in a leased system

ServiceRegistration reg = registrar.register();
Lease lease = reg.getLease();

The principal methods of the Lease object are


package net.jini.core;

public interface Lease {
    void cancel() throws 
                  UnknownLeaseException,
                  java.rmi.RemoteException;
    long getExpiration();
    void renew(long duration) throws 
                  LeaseDeniedException,
                  UnknownLeaseException,
                  java.rmi.RemoteException;
}
The expiration value from getExpiration() is the time in milliseconds since the beginning of the epoch (the same as in System.currentTimeMillis()). To find the amount of time still remaining from the present, the current time can be subtracted from this:

long duration = lease.getExpiration() - System.currentTimeMillis();

7.1.1 Cancellation

A service can cancel its lease by using cancel(). The lease communicates back to the lease management system on the lookup service which cancels storage of the service.

7.1.2 Expiration

When a lease expires, it does so silently. That is, the lease granter (the lookup service) will not inform the lease holder (the service) that it has expired. While it might seem nice to get warning of a lease expiring so that it can be renewed, this would have to be in advance of the expiration (``I'm just about to expire, please renew me quickly!'') and this would probably be impractical. Instead, it is upto the service to call renew() before the lease expires if it wishes the lease to continue. The servis in milliseconds. The expiration time is since the epoch, whereas the duration time is from now. Generally leases will be renewed and the manager will function quietly. However, the lookup service may decide not to renew a lease and will cause an exception to be thrown. This will be caught by the renewal manager and will cause the listener's notify() method to be called with a LeaseRenewalEvent as parameter. This will allow the application to take corrective action if its lease is denied. The listener may be null.

You have to be careful about setting the duration in renewFor() (in Jini v1.0). If you want the service to be registered forever, it is tempting to use Lease.FOREVER. However, the Jini 1.0 implementation just adds this to System.currentTimeMillis() which overflows to a negative value which is not checked. So it never does any renewals. Strictly, you need to check that


duration + System.currentTimeMillis() > 0
before calling renewFor(). This is fixed in Jini 1.1. The method renewUntil() can use Lease.FOREVER with no problems.

7.2. Granting and handling leases

All of the above discussion was from the side of a 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 which can be skipped for now: it is not needed until the chapter on ``Remote Events''. An example of use is given in the chapter on ``More Complex Examples''.

A lease can be granted for almost any remote service, any one where one object wants to maintain information about another one which is not within the same virtual machine. Being remote, there are the added partial failure modes such as network crash, remote service crash, timeouts and so on. An object will want the remote service to keep ``pinging'' it periodically to say that it is still alive and wants the information kept. Without this periodic assurance, the object may conclude that the remote service has vanished or is somehow unreachable, and it should discard the information about it.

Leases are a very general mechanism for one service to have confidence in the existence of the other for a limited period. Being general, it allows a great deal of flexibility in use. Because of the possible variety of services, some parts of the Jini lease mechanisms cannot be given totally, and must be left as interfaces for applications to fill in. The generality means that all 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 gives 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. Some simple possibilities are

  1. Always grant the requested time
  2. Ignore the requested time and always grant a fixed time
Of course, there are many more possibilities, based on expected time to live of the lessor, system load, etc.

There are other issues, though. Any particular lease will need a time-out mechanism. A group of leases can be managed together, and this can reduce the amount of overhead of managing individual leases.

7.2.1 Abstract Lease

An abstract lease gives a basic implementation of a lease, that can almost be used for simple leases. Warning: this class, and those that depend on it are still not fully specified, and may change in future versions of Jini.


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: firstly, the constructor is protected, so that constructing a lease with a specified duration is devolved to a subclass. This means that lease duration policy must be set by this subclass. Secondly, the renew() method calls into the abstract doRenew() method, again to force a subclass to implement a renewal policy. Thirdly, it does not implement the method cancel(), so that this must also be left to a subclass. So this class implements the easy things, and leaves all matters of policy to concrete subclasses.

7.2.2 Landlord Package

The ``landlord'' is a package to allow 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. (Warning: this is part of the com.sun.jini package, which may change for Jini 1.1.) 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 indentified 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 itelf, 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 lease wishes to cancel or renew itself, it 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 .

Figure 7.2: Class diagram in the landlord package
where the interfaces are shown in italicised font and the classes in normal font. This fairly complex set of classes and interfaces is driven by a number of factors
  1. A simple lease implementation was needed for a variety of situations. If a particular implementation is chosen, then a factory is needed for this, and a corresponding factory interface to generate other particular implementations. A lease (on the client) also requires the existence of some handler for its methods on the lease granting side, which is the landlord
  2. To handle all policy issues such as initial granting of lease times, and requests for lease renewal, a policy object is used. This needs something to work on, and to store results. The lease is over on the client side, and this needs to be done on the leasing side. The required information for this policy management is wrapped up in a leased resource
  3. For each lease on the client side there will be a leased resource on the lessor side. These must be stored and managed somehow. There may be a few leases, but there could be many thousands. There could be relationships between them (such as linear order), or none at all. So to avoid decisions about storage structures which will be wrong half of the time, lease management is just left as an interface

The interfaces assume that they will be implemented in certain ways, in that each implementation class will contain a reference to certain other interfaces. This doesn't show in the interface specifications! If we have a landlord for, say a Foo resource, then we could end up with the class structure of figure 7.3.

Figure 7.3: Class diagram in a landlord implementation
Some comments are appropriate about the directions and multiplicities:
  1. A landlord can be managing many leases. But it doesn't know what the leases are - they know their landlord, and call its methods using the lease cookie. So many LandlordLease objects contain a reference to a FooLandlord
  2. Certain requests need to get forwarded through the system. For example, a renew() request from a lease will get passed to a landlord. This cannot handle it directly, since this is a matter requiring policy decisions. It must be passed to a lease policy object. One way of doing this (as shown in figure 7.3) is for the landlord to have a reference to a lease manager which has a reference to a lease policy. Similarly, a newLease() request from the landlord will need to invoke a newLease() method on the factory, and this can be done by ensuring that the lease policy also has a reference to the factory
  3. A factory may be used by many lease policies; a policy may be used by many lease managers; a lease manager may be used by many landlords

7.2.3 Landlord Lease

The class LandlordLease extends AbstractLease. This has 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() is deferred to its landlord.

    public void cancel() {
	landlord.cancel(cookie);
    }

    protected long doRenew(long renewDuration) {
	return landlord.renew(cookie, renewDuration);
    }
The 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 this system the cookie is used as an identifier for the lease.

7.2.4 Leased Resource

A LeasedResource is a convenience wrapper around a resource that includes extra information and methods for use by landlords. It defines an interface


public interface LeasedResource {
    public void setExpiration(long newExpiration);
    public long getExpiration();
    public Object getCookie();
}
This 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, which has been given out to a client.

An implementation will typically include the resource that is leased, plus a method of setting the cookie. For 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




7.2.5 Lease Policy

A 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 includes a ``factory'' method leaseFor() to return a lease based on the policy and request.

An implementation of this policy is given by the LeaseDurationPolicy. This grants and renews leases based on constant values for maximum and default lease durations.


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 adddition 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 is to set a hard upper limit (which could be, say, Lease.FOREVER), while the default is if the client asks for a duration of Lease.ANY.

7.2.6 Lease Manager

The 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 renewed() methods of the policy. This sets or alters 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. This defines the interface


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 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 this will look after a set of leases (really, their resources) in some manner, by adding a new lease resource to its set 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, from the lease itself when its 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 an issue 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 this policy implementation, we are okay here.

A third issue is about 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 since it has knowledge of all the leases and their duration. Possible ways include

  1. A thread per lease, which will sleep and timeout when the lease should expire. This will need to sleep again if the lease has been renewed in the meantime
  2. A single sleeper thread sleeping for the minimum period of all leases. This may need to be interrupted if a new lease is created with a shorter expiration period
  3. A ``polling'' mechanism in which a thread sleeps for a fixed time and then cleans up all leases that have expired in the meantime
  4. A lazy method, in which no active thread looks for lease expiries, but just cleans them up if it comes across expired leases while doing something else (which is done by Outrigger with Entries)

The FooLeaseManager implements this third 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;
import java.util.Map;

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 Map cancelAll(Object[] cookies) {
	for (int n = cookies.length; --n >= 0; ) {
	    cancel(cookies[n]);
	}
	return null;
    }

    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);
			// replace this landlord.cancel(r.getCookie()) ;
			
                    }
                }
            }
        }
    }

} // FooLeaseManager

7.2.7 Landlord

The 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 of leases in ``batches'', and will not be dealt with here. The Landlord interface is


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. In the FooManager implementation it gets this policy from the FooLeaseManager. The cancel() method needs to modify the list of leases. In the FooManager implementation it passes this onto 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. 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 serialising the lease and sending it to this remote client. While serialising it, the landlord field will also be serialised 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, which by then will be remote from its origin. So 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



/**
 * 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;
import java.util.Map;

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 Map cancelAll(Object[] cookies) {
	return 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

7.3. Summary

Leasing allows resources to be managed without complex garbage collection mechanisms. Leases received from services can be dealt with easily using LeaseRenewalManager. Entities that need to hand out leases can use a system such as the landlord system to handle these leases.


If you found this chapter of value, the full book is available from APress or Amazon . There is a review of the book at Java Zone


This file is Copyright (©) 1999, 2000, 2001 by Jan Newmarch (http://jan.netcomp.edu.au) jan.newmarch@jan.newmarch.name.

This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v0.4 or later (the latest version is presently available at http://www.opencontent.org/openpub/). Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.