Activatable Jini services, Part 2: Patterns of use

The RMI activation framework can produce self-sufficient and flexible Jini services

1 2 3 Page 2
Page 2 of 3

Our previous code examples focused on implementing activation, and arranging for objects to deactivate. We implemented the JoinManager class -- defined in the Core Jini Specification -- which the Jini service uses to handle registrations with lookup services and maintain its leases with those lookup services. JoinManager assumes responsibility for those tasks, but when the object deactivates and is destroyed, so is its JoinManager instance. While inactive, the service would fail to listen to and join newly available lookup services. In addition, it would not renew its leases, causing them to expire and making the service inaccessible to subsequent clients.

The Collection of Jini Technology Helper Utilities and Services Specifications, introduced in version 1.1. of the JSK, define three services that help solve these problems.

The lookup discovery service

Since Jini services must continuously discover and join lookup services that interest them -- and discard unavailable lookup services -- activatable services must employ third parties to fulfill those responsibilities while dormant. As its name suggests, the lookup discovery service (LDS) introduced in JSK 1.1 helps Jini services discover lookup services.

Jini services can register their interest in lookup services with the LDS, which will notify the registered Jini service when appropriate lookup services become available. According to the Jini Lookup Discovery Service Specification, "While an entity is inactive, the lookup discovery service, running on the same or a separate host, would employ the discovery protocols to find lookup services in which the entity has expressed interest, and would notify the entity when a previously unavailable lookup service has become available."

The LDS only informs the Jini service of available lookup services. The Jini service must join them, and remove its registration from stale or discarded services. Therefore, an activatable service implementation needs to initiate when the Jini service receives an LDS notification. However, the underlying assumption is that new lookup services are rarely discovered in a djinn.

To take advantage of the LDS, a Jini service must first locate an instance of the LDS via a lookup service, then register with it. The LookupDiscoveryService interface defines just one method:

public interface LookupDiscoveryService
{
   public LookupDiscoveryRegistration register
    (
     String[] groups, 
     LookupLocator[] locators, 
     RemoteEventListener listener, 
     MarshalledObject handback, 
     long leaseDuration)
  throws RemoteException;
}

The LDS performs both group and locator discovery; the register() method consumes an array of groups or lookup locators. The LDS will notify the RemoteEventListener when it discovers lookup services. The notifications are sent as RemoteDiscoveryEvent, a subclass of RemoteEvent. The LDS sends only one RemoteDiscoveryEvent to a registered client per discovery. The sequence of events generated by the LDS is strictly increasing, meaning that each event has an ID number -- consecutive events have consecutive ID numbers. If a gap occurs between event sequences, the Jini service must "sync up" with the LDS; that is, it would have to request the full set of discovered lookup services from the LDS via the LookupDiscoveryRegistration.getRegistrars() method. A Jini service can also specify an object to be included in the remote events sent by the LDS. The client can use this hand-back object to, for instance, uniquely keep track of LDSs it receives events from. The LookupDiscoveryRegistration object returned by the register() method contains a lease to the LDS. The Jini service specifies the lease duration. The registration object is mutable: the Jini service can add or remove locators and groups, or notify the LDS if it wishes to discard lookup services. It can also retrieve an array of all the lookup services the discovery service has found on its behalf.

When a "well-behaved" Jini service receives a RemoteDiscoveryEvent, it should activate and immediately join that lookup service; it should not wait until some other event, such as a client remote method call, causes it to activate. By immediately establishing residency in many lookup services, the Jini service increases its availability to clients.

The lease renewal service

Jini services lease their residency in lookup services, and must periodically renew it. In addition, as we have seen above, many other Jini services make themselves available as lessors. This presents another issue for activatable services.

According to the Jini Lease Renewal Service Specification, "The client of a leased service may run into difficulties if that service deactivates. Unless the client ensures that some other process renews the client's leases while it is inactive, or that the client is activated before its lease begins to expire, the client will lose access to the resources it has acquired. This loss can be particularly dramatic in the case of lookup service registrations. A service's registration with a lookup service is leased -- if the service deactivates ... and it does not take appropriate steps, its registrations with lookup services will expire, and before long it will be inaccessible. If that service only becomes active when clients invoke its methods, it may never become active again, because at this point new clients may not be able to find it."

The "other process" mentioned in the above paragraph is the lease renewal service (LRS), another Jini service specified in JSK 1.1. While new lookup services rarely appear in a djinn, lease renewal is much more common and would likely waste the computational resources that are managed by activation. Thus, lease renewal can be delegated to the LRS.

To use the LRS, a service must locate it with lookup services. Thereafter a client can call its createLeaseRenewalSet() method:

public interface LeaseRenewalService
{
   public LeaseRenewalSet createLeaseRenewalSet (long leaseDuration)
      throws RemoteException;
}

The LeaseRenewalSet is a way to manage leases with the LRS. The Jini service leases a renewal set from the LRS for a specific amount of time. When the lease expires, the LRS stops renewing the leases in that set. An obvious catch-22 results: once a service becomes inactive, how can it renew its lease for the LeaseRenewalSet? It must activate to do so. The Jini service can ask the LeaseRenewalSet to send it a warning event before the lease expires; it can then activate and renew the set's lease. The setExpirationWarningListener() method, of the LeaseRenewalSet class, registers the warning request.

The renewFor() method adds leases to a set; you can specify how long the lease should remain in the set, i.e., how long the LRS should keep renewing it. In addition to having methods that both register a listener for the set's lease expiration and add leases to the set, LeaseRenewalSet features methods that add and remove listeners for events that indicate a lease-renewal failure, and obtain all the set's managed leases.

To summarize, an activatable service must initiate:

  1. When it receives a LookDiscoveryEvent. At that point, it must join the located lookup services, or discard lookup services no longer available.
  2. When it receives an ExpirationWarningEvent, at which time it must renew its lease for the LeaseRenewalSet that generated the event.

Listening for events: The event mailbox service

A Jini service need not immediately handle all remote events directed to it. A dormant activatable Jini service implementation may not want to activate every time it receives a remote event. The event mailbox service (EMS), the third helper service introduced in the JSK 1.1, allows a service to delegate event listening to this third-party process. The Jini Distributed Event Specification highlights as one of its design objectives the ability to interpose third-party objects into the event-notification chain. The EMS can be used to intercept events, store them, and forward them when the mailbox's client deems it convenient.

Once a lookup service produces the EMS reference, a Jini service can register with it, resulting in a MailboxRegistration:

public interface EventMailbox
{
   MailboxRegistration register (long leaseDuration)
      throws RemoteException;
}

The MailboxRegistration object will be associated with a lease that can be obtained with the getLease() method -- and can be passed by an activatable service into the lease renewal manager's renewal set. The registration object also contains a RemoteEventListener implementation. The getListener() method returns that object, which can then be passed into any method requiring a RemoteEventListener. The EMS will intercept events directed to this listener.

The mailbox queues up the events for the original listener until the mailbox's enableDelivery(RemoteEventListener targetListener) method is called. At that time, the target event listener consumed by this method will receive the events. For instance, when an activatable service starts up, it can call enableDelivery() to receive all events accumulated while dormant, and any new events. When the service decides to deactivate, it can call the mailbox's disableDelivery() method; the EMS will then resume intercepting and storing remote events while the service is inactive.

Saving state

The Jini Specification outlines not only how a service should tend to the continuous activities outlined above, but also how to maintain a persistent state across service restarts and crashes. This persistent state consists of the following information:

  • Service ID: Across all subsequent registrations, the service must use the service ID obtained from the first lookup service it registered with.
  • Attributes associated with the service in lookup services. These attributes must be consistent for all lookup service entries.
  • Groups the service wishes to participate in.
  • Specific lookup services to register with.

When a service deactivates, this information needs to be saved to persistent storage. Likewise, when the service activates, it must be retrieved to initialize the service. Other pieces of information, specific to a service implementation, may need to be similarly saved.

In Part 1, we saw how RMI's MarshalledObject allows initialization data to pass to the service via the activation constructor, whose implementation could then set the appropriate instance variables for the service. (In the example cited in Part 1, the parameter was the directory path to a bank's property description file.) Since the activation constructor features only one MarshalledObject parameter, one option is to create a special object that encapsulates all the initialization data needed for the Jini service. You could then obtain this information from the object's instance, returned by MarshalledObject's getObject() method. A more flexible approach would be to record the name of the directory in the MarshalledObject, then store the initialization information in that directory as serialized objects.

Summary

In this article, I highlighted the benefits of RMI activation for increasing the availability of Jini services. Activation offers a tool -- managed by an activation system process -- to the programmer that allows remote object implementations to be brought into and removed from execution. First, the programmer has to make an object activatable by outfitting it with an activation constructor, and arranging for it to be exported to the RMI runtime. This is easily accomplished by subclassing the java.rmi.activation.Activatable class. Then, a developer must register objects with rmid by specifying the object's activation group descriptor and activation descriptor. For each activation group, the activation system will spawn a Java VM, which runs as its child process, and will then initiate the activatable object in that VM. An object activates via the activation constructor, which allows initialization data to be passed to it. When an object is no longer active, it notifies the activation system via a call to Activatable.inactive(). Lastly, the activation system destroys the object, and may shut down the VM for the group in which the object was running.

This infrastructure supports high availability for Jini services for the following reasons:

  • The activation system (rmid) manages computational resources, making remote object servers (servers where Jini service implementations are running) more reliable, and requiring less system administration.
  • Activatable objects preserve remote references over restarts. If a service registers its proxy with lookup services, restarting the service -- or the entire object server -- causes no problems for the proxies; their references to the service will remain valid. Without activation, these remote references would become invalid (i.e., unusable by clients).
  • The activation system manages VMs for activatable services, so if a service crashes it will be restarted (activated) automatically. Also, if a server is rebooted, as long as rmid starts up, it will automatically make every service registered with it available for remote method calls.

In most computing environments, if a software or hardware component is available, it means the component is executing. But in the Jini context, even if a component is not executing, it is still available in the Jini federation as long as it holds valid registrations in lookup services. In order to ensure that availability, an activatable Jini service must follow these guidelines for good Jini citizenship:

  • Attend to its continuous responsibilities in the Jini federation: discover and register with lookup services, manage leases, and be responsive to remote events
  • Preserve its state across service restarts, including its service ID, groups it is interested in joining, the attributes stamped on its lookup service entry, and a set of lookup locators with which it wishes to perform unicast discovery
1 2 3 Page 2
Page 2 of 3