Activatable Jini services, Part 1: Implement RMI activation

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

A fundamental shift is taking place in the systems architectures of major software vendors. Presently, the industry aims to deliver services via software in such a way that users can interact with these services virtually anywhere, at any time, from any device. This new paradigm has been compared to a dial tone; practically anywhere in the world, one can pick up a phone and immediately hear a dial tone. Software services like banking, insurance, news, inventories, sales leads, and travel schedules may soon be as accessible as that familiar hum. IBM's WebSphere, HP's e-speak, and Microsoft's Microsoft .Net are all different manifestations of this ambitious desire, as is Sun's Jini.

TEXTBOX:

TEXTBOX_HEAD: Activatable Jini services: Read the whole series!

:END_TEXTBOX

The notion of a service lies at the heart of Jini. The Jini Specification defines a service as "... something that can be used by a person, a program, or another service ... Services will appear programmatically as objects in the Java programming language, perhaps made up of other objects ... A service will have an interface, which defines the operations that can be requested of that service. The type of the service determines the interfaces that make up that service." (See Resources for the Jini Specification.)

In Jini, a software component makes its services available through a proxy. Other services must locate that proxy to interact with a service within djinn, a federation of Jini services. Jini offers a robust mechanism, based on the Java language interfaces implemented by the proxy, that helps you look up proxies and other optional parameters. However, the ubiquitous computing environments promised by Jini and other service-oriented architectures require a service that is easily locatable and readily available.

For services with simple functionality, the proxy, once retrieved from the lookup service, might be able to operate independently -- making it a "strictly local" proxy. For instance, a service that converts decimal numbers into their hexadecimal equivalents would likely perform this computation entirely in the client's address space. For more involved services, the proxy might have to communicate with external resources running on other hosts. An example would be a currency converter, which must obtain up-to-date currency valuations to perform its service. For such a service, the proxy is not enough -- any other resources the proxy needs must also be on hand.

RMI in the Jini environment

The Jini Specification leaves the proxy, protocol, and server to communicate with the service implementation. The service implementation is hidden from the client, which only has to agree on the Java programming language interface, not a specific wire protocol, to interact with the service. Java Remote Method Invocation (RMI) can conveniently arrange such communication. All Jini services in Sun's implementation of the Jini Specification use RMI. Since Jini proxies can implement any part of the J2SE platform, of which RMI is a required component, any client VM using a Jini service proxy probably supports RMI.

RMI allows an object to make its method calls available to objects that reside in other virtual machines, and even on other hosts. An object that implements a subclass of java.rmi.Remote can export itself to the runtime to listen on a special port for incoming method invocations. A stub, typically generated by the rmic stub compiler that comes with the JDK, frees up methods specified in a Remote subtype and implemented from object to object. Once a stub is available to a client, that client can call methods on the stub. The stub then forwards method invocations to the remote object, and marshals and unmarshals method parameters as needed. In this scenario, a Jini service implemented as an RMI remote object will register its stub as the service object with lookup services. Figure 1 illustrates this process:

Figure 1: Client receives a service proxy from the lookup service, starts communication with the remote object

A loan approval service

To illustrate a Jini service that utilizes RMI, I have constructed a loan approval service for automobile buyers. This service collects information about the customer and the car he wishes to finance; it also indicates whether the bank will approve the loan, and if so, on what terms. If a bank implemented this service, it might present it as an applet on its Website, a module available via an ATM interface, a component accessed through a wireless PDA or cell phone, or simply as a voice interface on a telephone.

The LoanService interface has only one method:

      public interface LoanService 
      {
             public LoanApproval requestLoan(LoanRequest request) 
                  throws RemoteException;
      }

The LoanRequest object encapsulates information about the customer and the loan, such as the customer's annual income and credit report, the price of the car, and the requested term of the loan. The method returns a LoanApproval object, which provides details about the bank's terms for the loan, like the annual interest rate (APR) and the required amounts for the down payment and monthly installments. If the bank does not approve the application, the method returns a null value.

This service interface is not tied to RMI or any other specific implementation. It declares a java.rmi.RemoteException because we envision this service as operating in a distributed environment. Also, RemoteException is a useful class to account for exceptions that are due to network and communication failures. In order to deploy the service with RMI, we need to introduce a layer that extends java.rmi.Remote:

 
      public interface RemoteLoanService extends LoanService, 
            java.rmi.Remote 
      {
      }

A real-world implementation of this service would likely utilize an expert system to decide whether to write a loan. To keep this example simple, we will simulate this decision-making functionality with a Bank object. A property file stores the conditions that must be met for a loan to be approved. An instance of this object is initialized when the service starts up. Given a LoanRequest, the Bank object will determine if, and under what terms, the loan should be accepted. Figure 2 illustrates the service.

Figure 2: The loan approval service

RemoteLoanService is implemented as an RMI remote object, and therefore must accept method calls from other virtual machines. To do this, it exports itself to the RMI runtime, which in turn arranges for the object to listen on a special port for incoming method invocations. The java.rmi.server.RemoteServer class provides the semantics to create and export an object. In the RMI implementation introduced in JDK 1.1, once a remote object exports itself to the RMI runtime, it can accept remote method calls (i.e., stay active) "as long as the VM that created the object is running." (See Resources for the RMI Specification.)

UnicastRemoteObject, a subclass of RemoteServer, simplifies this approach to implementing remote objects. Follow these required steps to instantiate a service that is a subclass of UnicastRemoteObject:

  1. Create an instance of the object by calling one of its constructors
  2. Use the reference to this object and develop a service item
  3. Register the service item with Jini lookup services

For instance, our service implementation class would look like this:

public class LoanServiceImpl extends UnicastRemoteObject 
            implements RemoteLoanService 
{
        //Object that makes the decision about the loan
        Bank decisionSystem;
        //simple constructor to initialize the directoryPath
        public LoanServiceImpl(String path)
                  throws RemoteException
        {
                  super();                 
                  //initialize bank object from the property file
                   decisionSystem = initBank(path);
        }
        ....
              
        //implementation of LoanService
        public LoanApproval requestLoan(LoanRequest request)
                  throws RemoteException 
        {
                   //perform work here
                   return decisionSystem.decide(request);
        }
        //load Bank object 
        private Bank initBank(String filePath)
        {
                   //read property file from disk, initialize Bank object
                   ....
        }
        ...

The main() method of this class will create an instance of the object and register it with the Jini lookup service:

   public static void main(String[] argv) throws Exception 
   {
         //install an RMI security manager
         if (System.getSecurityManager() == null)
                    System.setSecurityManager(new RMISecurityManager());
         //collect directory path for bank property file on command line
         String path = argv[0];
                   
         //Create object. Note that we will need the remote interface,
         //represented by the stub.
         RemoteLoanService service = 
                  (RemoteLoanService) new LoanServiceImpl(path);
      //Join Jini lookup service
         JoinManager joinManager = new JoinManager(
                  service, 
                  null, 
                  serviceIDListener, 
                  null, 
                  null);
      }
} 

A skilled developer would declare every possible exception; however, to simplify the code presented here, we will just throw Exception.

The JoinManager class is a Jini helper utility. I will not discuss the semantics of using this class, but please note these important items:

  • The service might require some initialization data at startup. One example is the directory path to the bank's property file, which the command line specifies. The service ID is also initialization data; the first time a service joins a lookup, it receives a service ID. A ServiceIDListener implementation, which should save that ID to stable storage, notifies the object. Subsequent service registrations must reuse the ID; for instance, by implementing JoinManager's constructor, which allows the specifying of a ServiceID.
  • The service must accommodate some continuous activities. This includes constantly discovering and joining lookup services, renewing its leases in those services, and listening to events. An instance of JoinManager will take care of the first two duties. Since we passed a null instance in place of LookupDiscoveryManagement, JoinManager will use an instance of LookupDiscoveryManager to listen to discovery events from lookup services that are members of the public group. Similarly, since we specified null instead of LeaseRenewalManager, it will create and use one such object to manage leases.

The LoanServiceImpl object can be passed to a Java Virtual Machine (Java VM) for execution. We can run our loan service with a command line that specifies and passes properties to a Java VM, specifies the LoanServiceImpl class, and passes a parameter to the Java program itself. The properties include the Java security policy file required to run the LoanServiceImpl class, as well as the RMI codebase where clients that use the service's stub will download classes:

      /usr/java1.3/bin/java             -Djava.security.policy=/export/services/loanservice/loanservice.policy             -Djava.rmi.server.codebase=http://objectserver:8080/             LoanServiceImpl             /export/services/loanservice/bank.property

At this point, clients can locate the loan service proxy from lookup services. When a client calls the requestLoan() method, the stub will forward the method invocation to our LoanServiceImpl, which in turn will stand ready to process loan applications.

As long as the object is in an exported state, i.e., it can accept remote method calls, the VM will run continuously for objects that extend UnicastRemoteObject. Since LoanServicerImpl is a subclass of UnicastRemoteObject, the VM will not return after running the class's main method, but will instead continue running, allowing the object to accept remote calls.

RMI's architects foresaw the limitation of this approach: "Distributed object systems are designed to support long-lived persistent objects. Given that these systems will be made up of many thousands (perhaps millions) of such objects, it would be unreasonable for object implementations to become active and remain active, taking up valuable system resources for indefinite periods of time. In addition, clients need the ability to store persistent references to objects so that communication among objects can be re-established after a system crash, since typically a reference to a distributed object is valid only while the object is active."

That passage is from "Simple Activation for Distributed Objects" (see Resources), a paper written by Jim Waldo, Ann Wollrath, and Geoff Wyant. The paper also provides a solution to this problem and describes an implementation in the Modula 3 Network Object system. This work later found its way into Java and evolved into the Java activation architecture.

What activation buys us

In RMI terminology, "an active object is a remote object that is instantiated and exported in a Java VM on some system." (See Resources for the RMI Specification.) Running the main() method of our previous LoanServiceImpl class resulted in an active remote object, since it instantiated and exported an instance of LoanServiceImpl. As stated in the RMI Specification, "A passive object is one that is not yet instantiated (or exported) in a Java VM, but which can be brought into an active state. Transforming a passive object into an active object is a process known as activation."

The main goal, and primary benefit, of object activation is to allow remote object servers to manage their computational resources. According to the RMI Specification, "In RMI, activation allows objects to begin execution on an as-needed basis. When an activatable remote object is accessed (via a method invocation), if that remote object is not currently executing, the system initiates the object's execution inside an appropriate VM."

Using RMI activation, the system can also deactivate an object, by causing it to become passive and freeing up its computational resources. Sometimes the RMI activation can shut down the Java VM that runs the object. When a new method call is made to that object, the system initiates the object to turn active, and possibly creates a Java VM for it to execute in.

In addition to activating and deactivating objects on demand, the RMI activation framework also maintains remote object references over long periods of time. When a service registers its proxy with a lookup service, the proxy contains a reference to an RMI remote object implementation. If that implementation stops execution, the reference inside the RMI stub -- in the Jini service object -- becomes invalid. Even if the remote object restarts, the client can no longer use the stub. The service's only option is to reregister with lookup services.

Clients that have already obtained the service object must request a new copy -- possibly as part of handling the RemoteExceptions thrown at them when they are calling methods on the old, invalid stub. This tedious approach weakens the djinn, even with simple system administration and maintenance requirements. Activation ensures that the references contained in the service object remain valid across remote object implementations. Maintaining such persistent object references is another important benefit of the RMI activation framework.

The activation infrastructure

Developers can rely on the JDK's robust infrastructure to create and maintain highly available services. Activation allows us to register an object with a third-party system component, which will assume responsibility for starting and stopping the service. Once an object registers with this component, remote method calls on the object will not fail, even if the object is idle at that moment. Instead, this third-party component will start the object's execution.

JDK versions 1.2 and above come with such a system utility: the executable program rmid. This program is Sun's implementation of an activation system. (This executable is actually a wrapper around the class sun.rmi.server.Activation.) One activator process per host typically runs. The activation system performs two main responsibilities:

  • Tracking information needed to activate objects
  • Managing Java VMs in which the objects are activated

In addition to rmid, the JDK also provides an API, located in the java.rmi.activation package, that allows the programmer to interact with the activation system.

Figure 3: Classes of the activation API

In Figure 3, the Remote interface is a marker that indicates an object's remoteness. The RMI API provides an abstract class, RemoteObject, that implements this interface and provides correct remote object behavior for the equals(), hashCode(), and toString() methods. Remote reference semantics are supported by a subclass of RemoteObject called RemoteStub. All client stubs in the RMI system are descendants of this class.

On the server side, the API provides the abstract class RemoteServer. This class abstractly provides the semantics of creating and exporting a remote object. Thus, remote object implementations could subclass RemoteServer directly or, more likely, through one of its specified subclasses -- UnicastRemoteObject or Activatable. The former, introduced in JDK 1.1, provides for nonactivatable objects, while the latter, present in the JDK since version 1.2, facilitates the creation of activatable remote objects. It is not absolutely necessary for a unicast or an activatable remote object to extend either of these, though. Java's single-inheritance mechanism might prevent a subtype of another class from extending one of the remote server types. In that case, the programmer must ensure the correct remote semantics for equals() and hashCode(), then use UnicastRemoteObject's or Activatable's exportObject() methods to make the remote object available to the RMI runtime.

There are two distinct phases of using this API:

  1. An object registers with the activation system
  2. The activation system triggers the object in response to a remote method call, and uses the activation API to create an instance of the object

Therefore, in the life cycle of an activatable object, there are two distinct VMs: the setup VM, which registers the object with the activation system, and the server VM, created by rmid to run the activated object. The setup VM exits after performing the following tasks:

  1. Specifying to the activation system the VM in which the object will execute
  2. Specifying the object to the activation system
  3. Registering the object with the activation system, thereby obtaining the object's activation ID
  4. Exporting the object to the RMI runtime

The setup program does not have to create an object's instance to register the object with the activation system. When the setup VM exits, no instance of the activatable object will be running in any VM. It is the job of the activation system to fire up a VM on demand and arrange for it to load an instance of the object.

Activate the loan service

In order to participate in the activation process, an object must meet two requirements:

  1. It must register an activation descriptor with the activation system, or activator
  2. It must provide a special activation constructor

Once an object registers with the activation system, it is ready to participate in the activation protocol.

Figure 4: The activation protocol

Figure 4 illustrates, step by step, the activation protocol outlined in the RMI Specification. The remote object, at right, is initially inactive. The client obtains a service object from the Jini lookup service, which includes the remote server's stub. The client then attempts to make a method call on the stub. The stub checks to see if a live reference to the remote object is null. If it is, the faulting remote reference it contains contacts the activation system on the remote object server. The activation system uses the activation ID passed to it by the stub to access the activation descriptor associated with the object during the registration process.

This descriptor allows the activation system to check for a preexisting activation group for the object. If not, it will create one using the group's descriptor; this might involve forking a new Java VM for the group. The activation system delegates the object's creation to the activation group, which uses the activation descriptor to create the object, including possible bootstrap data contained in a MarshalledObject. Finally, the group returns a live new object reference to the activation group, which passes this live reference back to the remote stub. The activation group notes that the object is now in active state, so subsequent method calls to it will simply return the live reference.

Register with the activation system

The purpose of registering with the activation system is to provide enough information to activate or recreate the object. The abstraction for providing information about the object is the activation descriptor, ActivationDesc. It specifies the following information:

  • The object's class name and code location are specified as strings.
  • Data the object needs to bootstrap itself must be encapsulated into a java.rmi.MarshalledObject. An instance of MarshalledObject is created by passing any serializable objects to its constructor. A marshalled object differs from a plain serialized object because it contains the location from where the object's class file can be retrieved; it's annotated with the codebase URL. When the java.rmi.server.codebase property is set for the VM that creates the MarshalledObject instance, that property's value will be annotated into the MarshalledObject. The loan service, for instance, needs to initialize the bank property file's location on the filesystem.
  • The object's restart mode specifies whether the object should be set in motion as soon as the activation system comes up (eager activation) or on an on-demand basis, when the object receives the first remote method (lazy activation). The default is lazy activation.

ActivationDesc also specifies the object's activation group. The activation protocol uses the activation group description to determine which VM to activate the object in.

VMs on demand

The RMI activation specification states that "the system initiates the object's execution inside an appropriate VM." The abstraction for this VM is the activation group or ActivationGroup; objects in the same activation group activate in the same VM. When a new activation group is created, the activator process running on the host will spawn a new VM. That VM will run as a child process, allowing the activator to notice if the VM crashes, for instance. When the next method call to an object belonging to that VM arrives, the activator will restart the VM. Remember the command line we used to fire up our original service:

      /usr/java1.3/bin/java             -Djava.security.policy=/export/services/loanservice/loanservice.policy             -Djava.rmi.server.codebase=http://objectserver:8080/             LoanServiceImpl             /export/services/loanservice/bank.property

The activation system must specify similar information to any new VM that it spawns. The ActivationGroupDesc class provides a means to specify this material. This class offers immense flexibility: you can tell the activation system to fire up a virtual machine of your choice for each group, and to pass different properties, such as security policies, to each VM.

The ActivationGroupDesc allows us to specify the VM properties, typically passed via the -D command line option with a java.util.Property object:

      Properties vmProperties = new Properties();
      vmProperties.put("java.security.policy",
            "/export/services/loanservice/loanservice.policy");
      vmProperties.put("java.rmi.server.codebase", 
            "http://objectserver:8080/");

ActivationGroupDesc.CommandEnvironment also allows us to specify the command environment needed for the group's Java VM. For instance, we can specify the executable for the Java VM and command parameters (other than properties):

      ActivationGroupDesc.CommandEnvironment commandEnv = 
            new ActivationGroupDesc.CommandEnvironment(
                  "/usr/java1.3/bin/java", 
                  new String[] {"-Xmx48"});

We are now ready to register our group with the activation system:

      ActivationGroupDesc groupDesc = 
            new ActivationGroupDesc(vmProperties, commandEnv);
      ActivationGroupID groupID = 
            ActivationGroup.getSystem().registerGroup(groupDesc);

The registration returns ActivationGroupID, which uniquely identifies this group to the activation system. To run multiple Jini services in the same VM, we could save the group's ID to disk, so other activatable objects could use it when registering. The following shows the new main() method of the loan service implementation class:

  public static void main(String[] argv) throws Exception 
  {
    //collect directory path for bank property file on command line
    String path = argv[0];
       //specify group properties
       Properties vmProperties = new Properties();
       vmProperties.put("java.security.policy",
            "/export/services/loanservice/loanservice.policy");
       vmProperties.put("java.rmi.server.codebase", 
            "http://objectserver:8080/");
       //use default command environment 
       ActivationGroupDesc groupDesc = 
            new ActivationGroupDesc(vmProperties, null);
       ActivationGroupID groupID = 
            ActivationGroup.getSystem().registerGroup(groupDesc);
       //class name and location for object
       String className = "LoanServiceImpl";
       String codeLocation = "http://objectserver:8080/";
                         
       //bootstrap data for object
       MarshalledObject data = 
            new MarshalledObject("/export/services/loanservice/bank.property");
       //this constructor will also create the group
       ActivationDesc activationDesc = 
            new ActivationDesc(groupID, className, codeLocation, data);
       RemoteLoanService service = 
             (RemoteLoanService)Activatable.register(activationDesc);
   //Join Jini lookup service. Same as before.
      //We will change this later to delegate lease management, 
      //lookup discovery, and event handling.
      JoinManager joinManager = new JoinManager(
            service, 
            null, 
            serviceIDListener, 
            null, 
            null);
      }
  } 

As I mentioned earlier, one of the activation system's responsibilities is to serve as a database of activation information. Given an activation ID, it can determine if the object is active; if not, the activation system can retrieve enough information from the object's activation description to recreate the object. The activation system saves this registration information to stable storage.

rmid's log directory provides this persistence. When rmid initiates, it reconstitutes these mappings from its log files. Thus, an activatable object does not need to reregister when the activation system restarts. Any services that were previously registered with rmid remain available for activation after rmid restarts. Since many Jini services in the Jini Starter Kit come as activatable objects, you do not need to manually restart these services. For instance, the Jini lookup service, once registered with rmid, will restart automatically when rmid returns after a machine reboot. Therefore, it is not wise to delete rmid's log directory, especially in a production system, because hundreds of objects' registrations could be destroyed. As such, these objects could not handle remote method calls unless explicitly reregistered with the activation system -- by running their setup program, for instance.

Specify rmid's log directory with a command line such as:

      rmid -log LOG_DIR

In addition, rmid should be shut down as shown below to allow it to save in a persistent state:

      rmid -stop

The activation constructor

An activatable object must provide a special constructor to assist with activation. The activation system will call this constructor to instantiate the object. Activatable objects are easiest to implement as subclasses of java.rmi.activation.Activatable, another RemoteServer type. One of Activatable's constructors also manages the registering and exporting of the object. Thus, we will subclass the activatable LoanServiceImpl class from Activatable and equip it with an activation constructor:

public class LoanServiceImpl extends java.rmi.activation.Activatable
      implements RemoteLoanService 
{
  //Object that makes the decision about the loan
  Bank decisionSystem;
  //activation constructor
  public LoanServiceImpl(ActivationID id, MarshalledObject data)
            throws RemoteException 
  {
        super(id, 0);
        String path = (String)data.get();
        decisionSystem = initBank(path);
  }
        ....
              
  //implementation of LoanService
  public LoanApproval requestLoan(LoanRequest request)
             throws RemoteException 
  {
             //perform work here
             return decisionSystem.decide(request);
  }
  //load Bank object 
  private Bank initBank(String filePath)
  {
             //read property file from disk, initialize Bank object
             ....
  }
  ...

Persistent references

In addition to managing computational resources on object servers, activation also offers long-term maintenance of remote object references. When the client generates the method call to the remote service implementation, it assumes that the reference contained in the stub is valid; if it is not, the method call will result in a RemoteException. Without activation, the client application may retry the operation (hoping that it will succeed), reobtain the service proxy from the lookup service, or simply inform the user that the server connection is dead.

With an activatable service implementation, the stub will try to contact the object via a live remote reference, just as it would with a nonactivatable implementation. If the reference is not valid, the stub will use a second reference, this time to the activation system running on the remote object's host. This activation system is stored in the stub as an internal RMI reference. If it receives a request to activate an object, the stub will return to the caller -- the Jini client, in our case -- a live reference to the remote object. Thus, this second remote call will return a valid service reference to our applet. This is called a faulting remote reference, since it allows the activation system to "fault in" the object on demand. This process is transparent to the client and is solely an implementation issue of the service.

The following scenario illustrates the benefit of maintaining persistent references:

  1. Start the activatable service. It will register with rmid and Jini lookup services.
  2. Initiate the client. It will obtain the service object from a lookup service and use the object to make remote method calls to the service implementation.
  3. Suppose a system administrator reboots the machine that runs the remote service implementation, destroying the remote object and its Java VM in the process. If a client attempts to generate a method call to the server, it will receive a RemoteException, meaning the service is not available. A few minutes later, the remote object server returns. Since the system administrator configured rmid to fire up when the machine comes back (e.g., via the Unix init mechanism or as an NT service), rmid starts up, reads its log files, and reinitializes its state. Then if the client, which has been running continuously while the server was rebooted, makes a remote method call to the server, it will discover no live reference to the remote object. It will then consult the server's activation system, which will activate the object and return to the client a live reference to the object. Provided that the object can be activated, the method call will succeed.

Deactivating the service

I started out by saying that activation's greatest benefit is its management of system resources in long-running object servers. We also saw how the activation system executes objects on demand. The activation system will not deactivate objects automatically, though.

There is no standard way to measure whether an object is "heavily" or "lightly" used; it depends on the situation. As well, even if objects are rarely used but the server they're running on has a light load, it would make no sense to deactivate these objects. These are decisions a programmer or system administrator must make. For example, a service might have a separate thread running to measure use. When this thread determines that the object is idle, it calls Activatable.inactive(activationID) to inform the object's activation group. If no pending method calls to the object remain, the activation group will unexport the object, and inactive() will return true. If there are pending calls, the method will not be unexported. If the object is the only one running in the activation group, the activation system might shut down the group's VM as well.

If we want a service to discontinue its availability to the activation system, we can unregister it with a call to:

      Activatable.unregister(activationID)

Unlike an inactive object, an unregistered object will not activate when it receives remote method calls. A service's administrative functions should allow an administrator to unregister an object when shutting it down.

While you were asleep...

Now that our loan service is activatable, it is better able to manage its resources, to delegate its startup and shutdown to rmid, and to maintain persistent references over object incarnations. However, activation introduces a new set of issues that we must deal with.

Recall that registering the activatable service with the activation system returned a remote stub to the service without firing up an instance of the object. We used this stub to register the activatable service with the lookup service. The activatable service will need to renew its leases, even while inactive. Good Jini citizenship also requires that the activatable service discover and join new lookup services. Finally, the activatable service might be interested in events while it is inactive. If the service activated just to handle these responsibilities, it would be a waste of resources. These duties should be delegated to third-party services. The Jini Helper Utilities and Services Specification introduces a lease renewal service, a lookup discovery service, and an event mailbox service. Part 2 of this article will discuss how activatable Jini services can take advantage of these tools.

Frank Sommers is the founder and CEO of Autospaces, a startup focused on bringing Jini technology to the automotive software market. He has served as VP of technology at Nowcom Corporation, a company providing business infrastructure to the finance and insurance industries, since 1996. He has been programming in Java since 1995, after attending the first public demonstration of the language on the Sun Microsystems campus in November of that year. His interests include parallel and distributed computing, the discovery and representation of knowledge in databases, and the philosophical foundations of computing. When not thinking about computers, he composes and plays piano, studies the symphonies of Gustav Mahler, and explores the writings of Aristotle and Ayn Rand. Sommers would like to thank John McClain of Sun Microsystems for his valuable comments on this article.

Learn more about this topic

  • Sun's John McClain posted "The Process Architecture of the JSK and JSTK" to the Jini-users list. It presents a clear explanation of the role of RMI activation in Sun's implementation of the services that come with the JSK
  • Manifestations of a service-oriented computing landscape