Deploy code servers in Jini systems

Set up HTTP servers for dynamic code download

Jini is one of Java's most exciting applications. With dynamic download of behavior, a universal code-execution platform, and simple construction of distributed systems, it promises to bring us to an era in which disparate software and hardware can simply locate each other and interact, without determining the communication protocol in advance.

Jini's conceptual simplicity is impressive, but newcomers may find the complexity of setting up and running a Jini system daunting. Just to run a simple Jini system, you need to launch six or seven processes:

  1. The Remote Method Invocation Daemon, rmid
  2. A lookup service
  3. An HTTP server for the lookup service
  4. Your application-layer Jini service
  5. An HTTP server for your service
  6. The client for your Jini service
  7. If callback code for events needs to be downloaded: An HTTP server for the client

The following figure illustrates this deployment process.

Deployment diagram

These HTTP servers serve jar files or classfiles to the Jini client, which uses them to run the Jini proxy in its own process space; likewise, these servers can serve code on the client's behalf, for use in events.

HTTP servers, by themselves, are of course not Jini-specific; they are familiar from the World Wide Web. On the Web, the deployment scenario is simple: the clients make HTTP requests from the server. In a Jini system, though, any component may need code served on its behalf, and so we may have to set up many HTTP servers for even the simplest systems. As the Jini Testing Handbook at Jini.org puts it:

The codebase property and dynamic downloading of code can be a confusing hurdle for many Jini programmers.

We need a simple code-serving process. During development, we don't want to repeatedly stop and restart the system. Even after development, we'd like to simplify deployment of the system, with all its associated clients, services, and HTTP servers.

Several helpful solutions exist. I review these options for serving code, and discuss their pros and cons for various scenarios. In this article, I don't cover Jini basics, remote classloading principles, and other configuration problems, including log files, codebase definition, and security restrictions. (See Resources for information on these topics.) Instead, I focus on technologies useful in deploying a Jini system's code-serving portion. For this article, I assume you're using Jini's reference implementation.

Unreasonable solutions

Before reviewing some good solutions for Jini code-serving, let's eliminate some poor solutions. First, you may be tempted to give a file URL as a codebase (such as file:///usr/myjiniservice/lib/myjiniservice.jar), which may even work, but it limits your Jini system to one machine. Jini's whole point is to construct distributed systems, so don't use file URLs!

Second, since you typically run one HTTP server per machine, using port 80, you may be tempted to serve all the code for your Jini system from one HTTP server using one port, using all classfiles from one directory or serving them in one jar file. Don't do it. This setup may work for development, but when you deploy the system on multiple machines, you will have to divide the classfiles among the machines, which means different HTTP servers. Copying all classfiles to all machines would obviate the purpose of dynamic classloading: an application dynamically downloads the classes it needs. The complexity of multiple HTTP servers, each serving only the code relevant to one Jini service or client, may be daunting, but you shouldn't bypass it, even during development.

Code-serving options

Many HTTP-serving technologies are available, each with its own advantages, such as:

  • Ease of development
  • Ease of migration from development to deployment
  • Low memory and CPU burden
  • Portability
  • Close cohesion of Jini components with code served on their behalf, but loose coupling between different Jini components
  • Compatibility with RMI activation
  • Security
  • Enterprise-class Web application features

Let's look at some of these technologies.

Script the system

To launch even one process in a Jini system, you'll probably use a script, since typing the long command lines required is impractical. Next, you run the system by calling all the scripts for all the processes from a master script.

To run a simple Jini system, your script should do the following:

  • Delete old rmid log files and process-specific log files (for example, reggie, the lookup server in the reference implementation that generates its own log). These log files can help revive crashed processes for robustness, but during development, you may want to start with a clean slate each time.
  • Run rmid with the appropriate policy file for security definition.
  • Run an HTTP server for reggie. To start, you can use the simple HTTP server included in the Jini release. I'll discuss other HTTP servers, including Brazil, below.
  • Run reggie.
  • Run mahalo (for transactions) and other infrastructural services, each preceded by their respective HTTP server.
  • Run an HTTP server for your Jini service.
  • Run your Jini service.
  • If the service's client needs notification of remote events or must allow callbacks from the server, run an HTTP server for the service's client.
  • Run the client for your Jini service.

You need to arrange the temporal relation of these processes carefully. You should delete old files synchronously to eliminate the junk before starting other processes. You need to execute rmid before any RMI clients/servers start. Likewise, you must run each HTTP server before its accompanying Jini service/client starts, to avoid a confusing warning message.

Once running, an HTTP server and its Jini component need to run in parallel with other HTTP/Jini pairs. On the other hand, the order of operation of various Jini services and clients isn't important -- Jini's beauty is the dynamic discovery process that lets those services and clients respond to new Jini components entering the system.

During development, you need another script to kill components that you are developing, until those components reach the stability needed to automatically update. (You can either leave HTTP servers running in the background or stop and restart them.)

For Unix systems, you can write shell scripts, but for Windows, the anemic DOS batch-file language makes writing decent scripts difficult and creates cross-platform compatibility problems. If you stick to portable scripting languages like Python, Ruby, and Perl, you can easily switch platforms, although you may have to install a language interpreter.

Scripting has several advantages. First, writing scripts in an interpreted language is usually easier than writing code for the same purpose in Java. Second, scripting, as opposed to launching various Jini components and HTTP servers in one JVM, lets you easily split up the system when moving from one development machine to multiple deployment machines. Thus, a script is useful when you want to start quickly and then have flexibility in reconfiguring your system.

A Python, Ruby, or Perl script that runs multiple system components has disadvantages, since it entails the overhead of installing and running the scripting interpreter. Also, running each component as a separate process can create an excessive memory and processing burden. Each JVM can consume many megabytes of RAM even with the simplest programs. This can be problematic in memory-limited devices on which Jini often runs.

In-process HTTP server

Alternatively, you can start an HTTP server in a separate thread inside the same process as the Jini component.

In-process HTTP servers reduce the number of processes, minimizing the resource burden. This solution also creates the correct cohesion between a Jini component and the code served on its behalf, minimizing the chance that incompatible code will run in the proxy/stub and in the originating component. Moreover, in a security-conscious system with authentication, this also has the security advantage of reducing the number of processes that must mutually "trust" each other.

However, any in-process HTTP server presents a serious problem: it makes using activatable services difficult. An activatable service is deactivated by rmid, which then activates the service (sometimes in a new VM) when a client attempts to access it. If the HTTP server is in-process with the service, then the HTTP server is deactivated and activated together with the service. This may cause port-binding problems if two Jini components that include HTTP servers are then activated on the same machine. In addition, when a Jini client receives a Jini proxy from a lookup service, and then requests the proxy's classes from the HTTP server, the HTTP server may be inactive. In that case, of course, we expect the HTTP server to activate together with the Jini service, but this is impossible: until the service itself is accessed, the HTTP server remains inactive and unable to serve classes.

When launching an HTTP server in-process, be sure that its functionality meets your needs. Most in-process servers I describe below lack functionality found in enterprise-class HTTP servers, such as HTTPS and Web application modularity.

Choose the right in-process server

In choosing an in-process HTTP server, one obvious candidate is the minimal class server com.sun.jini.tool.ClassServer, found in the Jini reference implementation. Just construct the ClassServer with the appropriate web-root directory and an unused port. You should easily find the appropriate directory, since you are trying to serve code related to the component itself.

Although the ClassServer is conveniently bundled with the Jini reference implementation, and can serve classfiles, jar files, and individual classfiles from within a jar file, you can run any other Java HTTP server in-process. Indeed, many Java HTTP servers have a convenient instantiation API. Even without another API, you can always call the Java HTTP server's static main() method directly from your code.

You can choose from several lightweight HTTP servers designed for in-process use, each with its own special features. W. Keith Edwards, author of Core Jini, offers "The Service Writer's Toolkit" to simplify some common tasks for Jini developers. This toolkit's ClassExporter provides ClassServer functionality and goes beyond to automate some troublesome tasks typically involved in code server setup.

For example, ClassExporter saves you from manually setting up the codebase from the command line with a -D switch. Since the HTTP server runs in the same process as your Jini component, Edwards's class just sets the system property java.rmi.server.codebase to the local host address (overriding any command-line settings), thus giving the Jini proxy the appropriate codebase.

Edwards's class also lets you avoid configuring an available port. You can choose the port if you want, but if you call the default constructor, then that constructor binds the socket to any available port. For most network servers, setting the port arbitrarily at runtime may seem pointless: how can any client know which port to access if the port number is not well-known? In this case, however, the port number is part of the codebase, which travels with the Jini proxy and RMI stubs as an annotation on the serialized object. Any Java code that deserializes the object and looks for the codebase can read not only the home machine's address, but also the appropriate port.

ClassExporter is an abstract class with two concrete implementations: FSClassExporter ("FS" stands for "FileSystem") and ContextClassExporter. The former resembles a standard HTTP server in that it serves files from a given path on the disk. It can also serve classes from inside a jar file.

The ContextClassExporter, on the other hand, serves classes from its own classloader, after they have been loaded -- a neat trick. ContextClassExporter has the aesthetic appeal of serving code right from the source application, and goes a step further by sharing with the client the same code used by the Jini service, without the need to duplicate classfiles. However, ContextClassExporter has the potential security problem of offering all classes from the Jini component, not just the proxy and stub classes that should be downloaded. FSClassExporter and ordinary HTTP servers have an advantage on that score.

ClassExporter is inefficient in that it serves classfiles individually, rather than in a jar file. It also shares the RMI activation problem with any solution that serves classes from inside the Jini component's process.

Other HTTP servers designed for embedding in a Jini component are available. The DynaServer JavaBean from the Java Jini Tools Project at Jini.org can serve code from the classpath and automatically set the codebase, similarly to Edwards's ClassExporter. Sun's Brazil framework provides an API for dynamically serving content in HTTP (and other protocols). Brazil's design is lightweight enough to run in-process, yet easily extensible to provide exactly the enterprise-class functionality you need.

Unified interfaces for Jini systems

Several tools provide a single point for starting a Jini system. Such tools are useful for beginners, since they reduce the complexity of running multiple components individually.

1 2 Page 1
Page 1 of 2