Hinkmond is an engineer in Sun's Consumer and Embedded Division. He can be contacted at [email protected].
Jini connection technology is a Sun Microsystems invention designed to allow distributed systems of components to exist on many different platforms. Jini lets software and hardware components become seamlessly "federated" into a network through the use of Java technology. In this article, I'll show how you can use Sun's EmbeddedJava Application Environment tools to design and deploy a system that incorporates Jini technology into small memory footprint, network-enabled embedded devices.
Jini connection technology lets anyone connect any device to any network in a simple manner. It provides mechanisms for software services or hardware devices to automatically join together into a federation of Jini devices. The Jini services architecture is built upon the Java distributed computing platform architecture.
Devices in a Jini network are connected using Java Remote Method Invocation (RMI). This enables the Jini system to be secure and allows Java objects to move between Java Virtual Machines (VM) to implement the discovery protocol, join protocol, and the lookup service.
To form a Jini network of devices and services, a registration process occurs with a lookup service. When a device is connected to the network, it performs the discovery process and locates the Jini lookup service where it uploads all of its interfaces for all its services and thereby joins the Jini network. The lookup service also has the responsibility to behave as a control center to connect clients to a particular service. When that happens, the interface for the requested service is copied to the client.
For its part, the EmbeddedJava Application Environment is designed for small memory footprint embedded devices, each one having a dedicated functionality. It consists of a set of tools that allows the configuration and compilation of an environment that contains only the necessary methods and fields for a specified application. There are tools in the EmbeddedJava Application Environment that generate an executable image. This executable has a complete environment that can be placed into the ROM of an embedded system. EmbeddedJava developers use the set of tools found in the EmbeddedJava Application Environment to build a wide assortment of products such as pagers, mobile phones, instrumentation, controllers, printers, peripherals, and networking equipment.
The combination of the EmbeddedJava Application Environment and Jini is a natural one. Jini connection technology provides a way for small Java enabled devices to talk with each other, and the EmbeddedJava Application Environment enables you to build small devices that use the Java distributed computing platform.
Different Models for Using Jini Technology
There are many ways to set up a Jini network. The different methods are selected according to the requirements of the network. There are features in the current release of EmbeddedJava 1.0 that can be used to integrate Jini devices. But since a Jini device depends on the Java 2 platform, there must be a proxy connected to devices that use older Java platforms like EmbeddedJava 1.0, which is based on JDK 1.1.
For the easiest case to implement (that is, having a resident Java VM), the client needs to have a software proxy to a service that is located on a different machine running the Java 2 platform. This allows the service to take part in the lookup and discovery process of Jini connection technology (since it is built on the required Java 2 platform). The client must know beforehand how to contact the service and use its proxy software to make the necessary calls to the Jini service.
There are drawbacks for the case of having a resident Java VM on the client. You need a high-performance CPU, enough memory, and a permanent storage device on the client system. This is not a good requirement for smaller consumer devices.
Another possible design is to have a shared physical Java VM. This means that a higher performance system acts as a "bay" for Jini devices. This Jini bay should have a more powerful CPU, a good amount of memory, and a permanent storage device to handle the Java 2 platform. This enables the system to fully participate in the Jini lookup and discovery process. This Jini bay has a Java VM that is shared among many clients directly connected to it. The Java VM becomes the only resource for the Jini clients by requiring the Jini clients to know how to communicate with the Java VM on the Jini bay via a customized protocol.
The advantages of this model are that the Jini clients connected to the Jini bay don't need a full Java 2 platform. This enables the devices to be smaller and use lower-cost CPUs. The disadvantage is that there needs to be some connection (most likely a serial connection) made from the client to the Jini bay, which will require some limited customized protocol.
A third design is to have a Jini bay, existing somewhere on the network, that can talk directly over the Ethernet network to Jini devices. When a Jini client requests a special service, it communicates over the network with that particular Jini bay. This relationship is known only between the Jini bay and its client devices. To the rest of the Jini network, this is transparent because the Jini bay is acting as a proxy for its known devices to the rest of the federation. Here, I'll address this third design, with a Jini bay used as a proxy for an EmbeddedJava Application Environment client. This is the most likely case for using the Jini technology with EmbeddedJava 1.0.
Developing a Jini Application
To use the EmbeddedJava Application Environment to develop a Jini application, you need to proxy the Jini join-and-discovery process to the EmbeddedJava 1.0 client. This is done through a connection over the network (the third design model).
To design an EmbeddedJava application using the Shared Network Java VM model, you first need an interface from the EmbeddedJava client to a Jini proxy, which, in turn, can connect to a Jini network. Because the Jini proxy requires the Java 2 platform, all the Java 2 APIs are available for the Jini network join-and-discovery process. This gives flexibility for the proxy to handle the Jini environment while relaying pertinent information to and from the EmbeddedJava 1.0-based client to and from the Jini service.
The exact design of the communication protocol between the Jini proxy and EmbeddedJava 1.0 client is dependent on the type of data that is needed between the client and service. The design of the protocol could be as simple as a Java Socket connection over an Ethernet connection. I use a simple socket protocol in the example presented here.
After designing the EmbeddedJava 1.0 client to the Jini proxy interface, the next step is to design the Jini proxy to the Jini service interface. Because the Jini proxy acts as a regular Jini client, this is a straightforward Jini client/service design. For this example, the Jini client is a printer client to the UNIX lp command acting as a service with a Jini wrapper.
The steps to create the EmbeddedJava 1.0 Jini proxy client are:
1. Write a Java program for the Jini proxy client.
2. Use the JavaFilter tool to pare down a member list of dependencies to just the necessary methods and fields needed by the client.
3. Use the JavaCodeCompact tool to generate the EmbeddedJava intermediate C file.
4. Compile and link the EmbeddedJava intermediate C file into an executable image meant for the target device.
JavaFilter is an EmbeddedJava tool that produces a list of class members required by a specified Java program described by a set of Java compiled class files and a program specification. The member list is used by JavaCodeCompact in its -memberlist option to generate an intermediate C file containing only the Java class members that are needed to run the Java program.
The program specification is a list of command-line options that describe requirements for interfaces, entry points, class file location, and dependencies that are not automatically generated by JavaFilter's examination of class files (this happens in such cases as the names of classes referenced only by the method java.lang.Class.forName()).
You can then examine the member list generated by JavaFilter for repeated references to classes and any other anomalies before passing the list on to the JavaCodeCompact tool.
JavaCodeCompact takes Java class files and combines them into a target-specific C file. This C file stores the given Java classes in a preloaded internal format in its data structures. This C file is compiled and linked with the Java VM.
The target-specific C file produced by JavaCodeCompact, along with a Java VM, is compiled and linked by a native C compiler and linker to produce an executable on the target system. This executable contains within it the Java VM and the specified Java classes that make up the embedded Java program. This is the final step of producing an EmbeddedJava application.
Deploying Jini Applications
To deploy Jini applications, you must execute a Jini client, Jini optional proxy, Jini service, and Jini lookup service. For example, the Jini client presented here is an EmbeddedJava program run on a small device target system, and the example proxy, service, and lookup service are run on a Java 2 VM on a larger system that can handle the high demands of the Jini services and proxy.
To run the example, you need to complete these steps in order:
1. Start the Jini lookup service:
a. Start the Jini HTTP web server in Example 1(c).
b. Start the RMI daemon in Example 1(b).
c. Start the RMI registry in Example 1(c).
d. Start the Jini lookup service in Example 1(a).
2. Start the Jini service in Example 1(e).
3. Start the Jini proxy in Example 1(f).
4. Start the Jini client in Example 1(g).
A Jini Application Example
In the example I present here, the Jini service is a print service run on a UNIX system. The Jini service registers itself with a Jini lookup service. The print service allows a client to call through RMI to run UNIX shell commands to access the default UNIX printer connected to the network. The command can handle parameters, such as the name of a file to print and the printer name. The files that implement this print service -- PrintService.java, PrintRemote.java, JiniPrintClient.java, and JiniPrintProxy.java -- are available electronically; see "Resource Center," page 5.
The print service needs to have two parts. The first part is to use the Jini JoinManager() method to register itself with the Jini lookup service. This is done with a Jini method call, such as in Listing One. This allows the print service to find the instance of a Jini lookup service on the network. It attempts to join the lookup service and places its print service in the lookup registry according to the time set by the lease manager parameter object. The lease manager object prevents a service from becoming stale if it should die and not continue to renew its lease.
The second part of the print service involves creating the interface to the printer UNIX shell commands. This is done by using a serialized object that will be marshaled to the Jini client through a socket. To create the print-service interface, follow these steps:
1. Write a Java interface that extends Remote; see Example 2(a).
2. Compile the class file with the javac compiler; see Example 2(b).
3. Compile the class file with the RMI compiler; see Example 2(c).
Two files will be generated: PrintRemote_Stub.class and PrintRemote_Skel.class. These two RMI files are used by the Jini network to move the PrintRemote object to the Jini client. In the example print service, the Java class extends PrintRemote and implements the calls for print() and checkQueue() to call the wrapper around the UNIX commands lp and lpq. In the Jini client, the PrintRemote object is transmitted by the proxy to the Jini client by using the writeObject() method on one end and readObject() method on the other over a socket connection. There is a cast made to the PrintRemote interface of the remote object coming from the proxy. The client can then call either the print() or checkQueue() methods at the print service.
In the client, there are two steps that need to occur:
1. Get the print service remote object from the proxy through a socket; see Listing Two.
2. Call the print service method through the remote object interface; see Listing Three.
This completes a Jini client to Jini service example using a proxy that marshals the remote object from a Jini-enabled Java 2 VM system to a Jini-enabled EmbeddedJava 1.0 Application Environment client. By completing this connection, the final outcome is a Jini network utilizing the EmbeddedJava Application Environment to create a Jini Application built with EmbeddedJava 1.0 and Jini 1.0.
Acknowledgment
I would like to thank Saeed Mokhtarani for help with the print service example.
DDJ
Listing One
import jini.net.JoinManager; ... // PrintService extends UnicastRemoteObject and implements // PrintRemote, Serializable PrintService service = new PrintService(); ServiceInfo serviceInfo = new ServiceInfo(PRODUCT, MANUFACTURER, VENDOR, VERSION, "",""); BasicServiceType basicServiceType = new BasicServiceType("PrintService"); entry = new Entry[] {serviceInfo, basicServiceType}; try { joinManager = new JoinManager(service, entry, new ServiceIDHandler(), new LeaseRenewalManager()); } catch (java.io.IOException ioe) { }
Listing Two
public PrintRemote getPrintService() { Socket socket; InputStream inputStream; ObjectInputStream objInStream; try { // Open the socket for the ObjectInputStream socket = new Socket("<host>", PrintRemote.SOCKET); inputStream = socket.getInputStream(); objInStream = new ObjectInputStream(inputStream); // Read the PrintRemote object from the server service = (PrintRemote) objInStream.readObject(); // Close the socket socket.close(); } catch (Exception e) { e.printStackTrace(); } return(service); }
Listing Three
public String printToJiniService(String printerName, String fileName) { String retStr = null; if (service == null) { // Get the PrintRemote object from the server service = getPrintService(); } try { retStr = service.print(printerName, fileName); } catch (Exception e) { e.printStackTrace(); } return(retStr); }
Copyright © 1999, Dr. Dobb's Journal