Object mobility in the Jini environment

Reduce the complexity of installation and configuration of complex Jini applications with mobile code

1 2 3 Page 2
Page 2 of 3

A better solution would be to tell the VM where it can download the object's class from. One way to do this is to stamp the code location for the class onto the serialized object stream -- in other words, to annotate the serialized object with the codebase URL. This method facilitates dynamic code mobility, because the VM can decide at runtime where it should download the classes from. This is also the fundamental technique used by Java RMI (see the Java Remote Method Invocation Specification, Chapter 10.3.1).

Figure 3. Codebase annotation on the serialized object stream

The RMI runtime mechanism provides a special output stream, which, in addition to serializing the object, does the following:

  1. It annotates the location of the object's class to the serialized stream.
  2. When the object to be serialized is a remote object (i.e., when it implements java.rmi.Remote), instead of serializing the object itself, its stub is serialized.

The codebase is specified with the following property to the VM:

-Djava.rmi.server.codebase=CODEBASEURL

The codebase property is a space-separated list of URLs. When deserializing an RMI stub annotated with the codebase, the RMI runtime will create a class loader for each codebase URL specified in this list. (The class java.rmi.RMIClassLoaderis a factory of class loaders that facilitates this process.)

Jini services that use RMI to communicate between the service object and the service typically register their stubs in lookup services. (Recall that the RMI runtime replaces the object with its stub in the serialized stream.) This ensures that the RMI runtime annotates the serialized stub with the codebase for class downloading, and that this information will be available to clients of the service. The clients will use these URLs to download needed classfiles. The ability to download code is a key RMI feature. Jini builds on RMI's semantics of mobile code (see the Jini Architecture Specification, Section AR2.1.3).

The role of MarshalledObject

The semantics of annotated codebases is useful even outside the RMI runtime environment -- in other words, for objects that are not transferred from one JVM to another in the context of an RMI call. In a dynamic environment, when objects are transferred from one JVM to another, you will likely need to transfer the required classes for these objects as well. Since they do not benefit from codebase annotations of the RMI runtime, they need to offer codebase information to client JVMs in a different way.

The java.rmi.MarshalledObject class is used for that purpose (see the Java Remote Method Invocation Specification, Section 7.4.9). Creating a MarshalledObjectinstance involves passing a serializable object to its constructor. MarshalledObjectuses the special output stream of the RMI system that obtains the codebase property specified for the running VM, and saves this information along with the original object's serialized form. MarshalledObject's get()method returns the original object by downloading the needed classfile and reconstructing the object's state. Comparing two MarshalledObject instances produces equality if the two refer to the same object, regardless of the codebases specified.

When a service registers with a lookup service, it provides a ServiceItemto the lookup service's registrar. The service item contains the service ID, the service object, and a set of attributes associated with the service.

Each ServiceItem can hold an array of Entryobjects. Each Entry specifies a set of attributes. Each attribute must be declared with reference types (no primitives allowed), and must be serializable objects. When an Entryis serialized, each field it declares is serialized separately as a MarshalledObject (see the Jini Core Platform Specification, Section EN.1.3). This also facilitates code mobility: when a client retrieves the service item with a lookup operation, it can reconstruct each Entry object.

Setting your codebase right

The key to making code mobility work in Jini is to specify the correct service codebase, and then to make sure that you can actually download the classfiles needed from these specified locations. Specifically, you need to cause the correct codebase to be annotated by the runtime system in serialized objects inside ServiceItems before a service registers with lookup services.

The easiest way to achieve this is to run one or more HTTP servers for each service, and then to specify the URLs to these servers to the VM that runs the Jini service. For example, if code is located on a machine named classserver in directory /export/home/classes_for_my_service, and the HTTP server is running on that machine's port 8080 with its root directory pointing to /export/home, you would specify the following property (note the trailing slash at the end of the line):

-Djava.rmi.server.codebase=http://classserver:8080/classes_for_my_service/

That property will cause the VM running the service to use the URL for codebase annotations of objects passed via RMI calls, or as MarshalledObjects. When the service registers with lookup services, the service object and the Entry for the service will point to the appropriate location. Recall that class loaders form a hierarchy, and that the VM first asks the bootstrap class loader for the class, then asks each class loader in the order of their hierarchy until one of them loads the requested class.

Therefore, the classfiles that the client downloads should notbe available in the codebase of the client VM's bootstrap class loader, some other class loader that is ahead in the RMIClassloader hierarchy, or the class loader intended to load classes over the network. If the requested class can be loaded from local codebases, then code mobility will be circumvented. Therefore, you should not place classes that need to be downloaded in the CLASSPATH.

Developers new to Jini often encounter a situation in which their services work during development on the local machine, but stop working when they are deployed in a distributed environment. Most likely, during development, the classes were loaded locally all along, while at deployment these classes are no longer available at the remote client.

Figure 4. Deploying a Jini service

In Figure 4, the downloadable code needed by clients is placed in a special dl jar file, which is served by an HTTP server. The URL to this downloadable jar file is the codebaseproperty for the service's VM.

Deploying a Jini service

To make this discussion concrete, imagine a situation in which you just created what you think is an extremely useful Jini service. Since you just created the service, no one else has any code available to run it. In fact, at this point, no one even knows your service exists. Your goal is to make this service as widely available as possible to other Jini services or to human users.

Your first step is to start up your service. Alternatively, you can make the service available as an activatable RMI implementation (see the Java Remote Method Invocation Specification, Section 7). If your service is written in Java (it does not have to be), the classfiles needed to run the application must be available to the local VM. There is nothing special here, since this is true of any Java program: the VM will start executing a main() method somewhere, and will load and link classes as it needs them via its class loaders. The best way to make these classes available is to include them in a jar file, and then ensure that this jar file is available to a class loader on the local VM. In other words, it has to be in the classpath for the local VM. (A convenient way is to set this jar file up as an executable jar.) No object mobility is involved at this point.

Next, you need to ensure that clients can obtain the necessary code to interact with your service. If you implemented your service using RMI, then the proxy object representing your service in the lookup service will likely be the RMI stub. Recall that when a stub is serialized via the RMI runtime, the codebase from which its classes can be downloaded is annotated in the object stream. As I discussed earlier, this codebase is obtained by the RMI system from the running VM's java.rmi.server.codebase property.

There are three steps necessary to make classfiles available to clients:

  1. Consider the classes the client needs to interact with your service, and create a separate jar file from these classes. By convention, this jar file is named servicename-dl.jar, where dl stands for downloadable (substitute the name of your own service for servicename). Every Jini service in the JSK is organized in this way. For instance, the lookup service implementation in the JSK consists of two files: reggie.jarand reggie-dl.jar. Any class that the client might need to load but does not have (because it is not part of the JDK version you're using) must be in the dlfile, including any exception classes specified by your service object. You can put any net.jini classes the proxy depends on in the dl file as well.
  2. Make available some mechanism for clients to download the

    dl

    file. The easiest way is to run an HTTP server and place the

    dl

    jar file in a directory served by that server. Alternatively, you can put the

    dl

    file on an FTP server, or a shared filesystem. However, HTTP is by far the easiest way to make code downloading work. Using file URLs is usually a very bad idea, because the file systems they refer to will not likely be available to Jini clients. The JSK includes a simple HTTP server suitable for this purpose. You can run this server with the command:

    java -jar MY_JINI_DIRECTORY/lib/tools.jar -port PORT_NUMBER -dir SERVER_ROOT_DIR

    An additional -verbose parameter will also print out each download request.

  3. When starting up the service, specify the URL to the dlfile as the java.rmi.server.codebase property. Thus, to start up your service, you might use a command line like this:

    java -cp LOCALLY_NEEDED_CLASSES/myservice.jar   -Djava.rmi.server.codebase=http://myhttpserver:8080/myservice-dl.jar   -Djava.security.policy=my_security_file   ... [OTHER PARAMETERS] ...
      myservice_main_class

    For an activatable service, the codebase information needs to be available to the VM for the activation group that runs the service (see "Activatable Jini Services").

You are ready to register your service with lookup services:

  1. Create a ServiceItem object to represent the service in the lookup service. Your service objects, along with Entryobjects in the service's attribute set, will be annotated with the codebase value specified to the VM (see above). Thus, when clients obtain the service item, they will be able to download the needed code from that location.
  2. Discover a lookup service and obtain its ServiceRegistrarobject.
  3. Pass the service item to the registrar's register(ServiceItem serviceItem, long leaseDuration)method, thereby obtaining the ServiceRegistration object corresponding to this registration. You can use the registration object to renew your service's leases, and in general to maintain the relationship between your service and the lookup service.

Note that your service needs to load classes to interact with the lookup service. Now your service becomes a client of the lookup service and needs to download the necessary classes from the lookup service so it can instantiate the lookup service's proxy. Your service will download the reggie-dl.jar file from the lookup service's codebase. To be able to load and link these classes, your service needs to grant the required permissions to its VM. It is beyond the scope of this article to discuss how to configure security policy files to allow this. The JSK contains several example policy files in the /example directory.

To summarize, your service uses the following jar files:

  1. myservice.jar(its own classfiles), in addition to any other package it might use. These need to be in the classpath of the service's local VM.
  2. jini-core.jarand possibly jini-ext.jarto interact with the Jini environment (perform discovery, use leasing, participate in transactions, and so on.) Alternatively you could place the necessary classes in myservice.jar.
  3. myservice-dl.jar, which consists of files the client will need to interact with the service. This jar file will be downloaded from a codebase specified by your service. This codebase must be specified with the java.rmi.server.codebaseproperty to the service's VM.

The client's view

You are now ready to take up the client's view. This article's premise is that code mobility reduces installation and configuration of otherwise complex applications in a Jini environment. Jini clients discover services in a Jini federation based on three possible attributes: the service's unique service ID, the service's programming language type, and any attributes the service may register as Entrys in the lookup service (see the Jini Core Platform Specification, Section DJ.2).

The most important of these is the programming language type. Since the client is another Java program (or a non-Java program that knows how to call methods on a Java object), it needs to know something about the contract the service interface specifies between the client and the server. This contract is expressed in terms of both Java code and that code's semantic definition -- neither of these would suffice alone. You might have a Java interface with this definition:

1 2 3 Page 2
Page 2 of 3