Chapter 1: Overview of Jini

Jini grew from early work in Java to make distributed computing easier. It intends to make ``network devices'' and ``network computing'' into standard components of everyone's computing environment. When you buy a new piece of office computing equipment such as a desk lamp, or a new home computer appliance such as an alarm clock, it will not only carry out its ``traditional'' functions but will also join into a network of other computer devices and services. The desk lamp will turn itself off when you leave your desk, informed by sensors in your chair; the alarm clock will tell your coffee maker to switch on a few minutes before it wakes you up.

Homes, offices and factories are becoming increasingly networked. Current twisted pair wiring will remain, but will be augmented by wireless networks and networks built on your phone lines and power cables. On top of this will be an infrastructure to allow devices to communicate. TCP/IP will be a part of this, but will not be enough. There will need to be mechanisms for service discovery, for negotiation of properties, and for event signalling (``my alarm has gone off - does anyone want to know?'').

Jini supplies this higher level of interaction. This chapter gives a brief overview of the components of a Jini system and the relationships between them.

1.1. Jini

Jini is the name for a distributed computing environment, that can offer ``network plug and play''. A device or a software service can be connected to a network and announce its presence, and clients that wish to use such a service can then locate it and call it to perform tasks. Jini can be used for mobile computing tasks where a service may only be connected to a network for a short time, but it can more generally be used in any network where there is some degree of change. There are a large number of scenarios where this would be useful:

  1. A new printer can be connected to the network and announce its presence and capabilities. A client can then use this printer without having to be specially configured to do so.

  2. A digital camera can be connected to the network and present a user interface that will not only allow pictures to be taken, but it can also be aware of any printers so that the pictures can be printed.

  3. A configuration file that is copied and modified on individual machines can be made into a network service from a single machine, reducing maintenance costs.

  4. New capabilities extending existing ones can be added to a running system without disrupting existing services, or without any need to reconfigure clients.

  5. Services can announce changes of state, such as when a printer runs out of paper. Listeners, typically of an administrative nature, can watch for these changes and flag them for attention.

Jini is not an acronym for anything, and does not have a particular meaning. (though it gained a post hoc it gained an interpretation of ``Jini is not initials''.) A Jini system or federation is a collection of clients and services all communicating by the Jini protocols. Often this will consist of applications written in Java, communicating using the Java Remote Method Invocation mechanism. Although Jini is written in pure Java, neither clients nor services are constrained to be in pure Java. They may include native code methods, act as wrappers around non-Java objects, or even be written in some other language altogether. Jini supplies a ``middleware'' layer to link services and clients from a variety of sources.

When you download a copy of ``Jini'', you get a mixture of things. Firstly, Jini is a specification of a set of middleware components. This includes an API (Application Programmer's Interface) so that you as a programmer can write services and components that make use of this middleware. Secondly, it includes an implementation (in pure Java) of the middleware, as a set of Java packages. By including these in the classpath of your client or service you can invoke the Jini middleware protocols to join in a Jini djinn. You also get source code to these packages as a bonus! Finally, Jini requires a number of ``standard'' services, and Sun gives basic implementations of each of these. These implementations are not an official part of ``Jini'', but are included to get you going. In practice, most users are finding these sufficient to do substantial work with Jini.

1.2. Components

Jini is just one of a large number of distributed systems architectures, including industry-pervasive systems such as CORBA and DCOM. It is distinguished by being based on Java, and deriving many features purely from this Java basis. One of the later chapters discusses bridging between Jini and CORBA, as an example of linking these different distributed architectures.

There are other Java frameworks from Sun which would appear to overlap Jini, such as Enterprise Java Beans (EJBs). EJB's make it easier to build business logic servers, while Jini could be used to distribute these services in a ``network plug and play'' manner.

The reader should be aware that Jini is only one competitor in a non-empty market. What will condition the success or failure of Jini is partly the politics of the market, but also (hopefully!) the technical capabilities of Jini, and this book will deal with some of the technical issues involved in using Jini.

In a running Jini system, there are three main players. There is a service, such as a printer, a toaster, a marriage agency, etc. There is a client which would like to make use of this service. Thirdly, there is a lookup service (service locator) which acts as a broker/trader$#47;locator between services and clients. There is an additional component, and that is a network connecting all three of these, and this network will generally be running TCP/IP. (The Jini specification is fairly independent of network protocol, but the only current implementation is on TCP/IP.) See figure 1.1.

Code will be moved around between these three pieces, and this is done by marshalling the objects. This involves serializing the objects in such a way that they can be moved around the network, stored in this ``freeze-dried'' form, and later reconstituted by using included information about the class files as well as instance data. This is done using Java's socket support to send and receive objects.

In addition, objects in one JVM (Java Virtual Machine) may need to invoke methods on an object in another JVM. Often this will be done using RMI (Remote Method Invocation), although the Jini specification does not require this and there are many other possibilities.

Figure 1.1: Components of a Jini system

1.3. Service Registration

A service is a logical concept such as a blender, a chat service, a disk. It will turn out to be usually defined by a Java interface, and commonly the service itself will be identified by this interface. Each service can be implemented in many ways, by many different vendors. For example, there may be Joe's dating service, Mary's dating service or many others. What makes them the ``same'' service is that they implement the same interface; what distinguishes one from another is that each different implementation uses a different set of objects (or maybe just one object) belonging to different classes.

A service is created by a service provider. A service provider plays a number of roles:

  1. It creates the objects that implement the service

  2. It registers one of these - the service object with lookup services. The service object is the ``publically visible'' part of the service, and will be downloaded to clients

  3. It stays alive in a server role, performing various tasks such as keeping the service ``alive''.

In order for the service provider to register the service object with a lookup service, the server must first find the lookup service. This can be done in two ways: if the location of the lookup service is known, then the service provider can use unicast TCP to connect directly to it. If the location is not known, the service provider will make UDP multicast requests, and lookup services may respond to these requests. Lookup services will be listening on port 4160 for both the unicast and multicast requests. (4160 is the decimal representation of hexadecimal (CAFE - BABE). Oh well, these numbers have to come from somewhere.) When the lookup service gets a request on this port, it sends an object back to the server. This object, known as a registrar, acts as a proxy to the lookup service, and runs in the service's JVM (Java Virtual Machine). Any requests that the service provider needs to make of the lookup service are made through this proxy registrar. Any suitable protocol may be used to do this, but in practice the implementations that you get of the lookup service (e.g from Sun) will probably use RMI.

What the service provider does with the registrar is to register the service with the lookup service. This involves taking a copy of the service object, and storing it on the lookup service as in figures 1.2, 1.3 and 1.4.

Figure 1.2: Querying for a service locator
Figure 1.3: Registrar returned
Figure 1.4: Service uploaded

1.4. Client Lookup

The client on the other hand, is trying to get a copy of the service into its own JVM. It goes through the same mechanism to get a registrar from the lookup service. But this time it does something different with this, which is to request the service object to be copied across to it. See figures 1.5, 1.6, 1.7, and 1.8.

Figure 1.5: Querying for a service locator
Figure 1.6: Registrar returned
Figure 1.7: Asking for a service
Figure 1.8: Service returned

At this stage there is the original service object running back on its host. There is a copy of the service object stored in the lookup service, and there is a copy of the service object running in the client's JVM. The client can make requests of the service object running in its own JVM.

1.5. Proxies

Some services can be implemented by a single object, the service object. How does this work if the service is actually a toaster, a printer, or controlling some piece of hardware? By the time the service object runs in the client's JVM, it may be a long way away from its hardware. It cannot control this remote piece of hardware all by itself. In this case, the implementation of the service must be made up of at least two objects, one running in the client and another distinct one running in the service provider.

The service object is really a proxy, which will communicate back to other objects in the service provider, probably using RMI. The proxy is the part of the service that is visible to clients, but its function will be to pass method calls back to the rest of the objects that form the total implementation of the service. There isn't a standard nomenclature for these server-side implementation objects. I shall refer to them in this book as the ``service backend'' objects.

The motivation for discussing proxies is when a service object needs to control a remote piece of hardware that is not directly accessible to the service object. However, it need not be hardware: there could be files accessible to the service provider that are not available to objects running in clients. There could be applications local to the service provider that are useful in implementing the service. Or it could simply be easier to program the service in ways that involve objects on the service provider, with the service object being just a proxy. The majority of service implementations end up with the service object being just a proxy to service backend objects, and it is quite common to see the service object being referred to as a service proxy. It is sometimes referred to a the service proxy even if the implementation doesn't use a proxy at all!

The proxy needs to communicate with other objects in the service provider. It appears we have a chicken-and-egg situation: how does the proxy find the service backend objects in its service provider? Use a Jini lookup? No, when the proxy is created it is ``primed'' with its own service provider's location so that when run it can find its own ``home''. This will appear as in figure 1.9

Figure 1.9: A proxy service

How is the proxy primed? This isn't specified by Jini, and can be done in a large number of ways. For example, an RMI naming service can be used such as rmiregistry, where the proxy is given the name of the service. This isn't very common, as RMI proxies can be passed more directly as returned objects from method calls, and these can refer to ordinary RMI server objects or to RMI activatable objects. Or the proxy can be implemented without any direct use of RMI, and can then use an RMI exported service or some other protocol altogether such as ftp, http or a home-grown protocol. These various possibilities are all illustrated in later chapters.

1.6. Client Structure

Internally a client will look like

Pseudocode Where discussed
prepare for discovery Discovering a lookup service
discover a lookup service Discovering a lookup service
prepare a template for lookup search Entry objects and Client search
lookup a service Client search
call the service

The following code is simplified from the real case, by omitting various checks on exceptions and other conditions. It attempts to find a FileClassifier service, and then calls the method getMIMEType() on this service. The full version is given in a later chapter. We don't give detailed explanations right now, this is just to show how the above schema translates into actual code.

 package nonworking;

public class TestUnicastFileClassifier {

    public static void main(String argv[]) {
	new TestUnicastFileClassifier();
    }

    public TestUnicastFileClassifier() {
	LookupLocator lookup = null;
	ServiceRegistrar registrar = null;
	FileClassifier classifier = null;

        // Prepare for discovery	
        lookup = new LookupLocator("jini://www.all_about_files.com");

        // Discover a lookup service
        // This uses the synchronous unicast protocol
	registrar = lookup.getRegistrar();

        // Prepare a template for lookup search
	Class[] classes = new Class[] {FileClassifier.class};
	ServiceTemplate template = new ServiceTemplate(null, classes, null);

        // Lookup a service
	classifier = (FileClassifier) registrar.lookup(template);

        // Call the service
	MIMEType type;
	type = classifier.getMIMEType("file1.txt");
        System.out.println("Type is " + type.toString());
    }
} // TestUnicastFileClassifier

 

1.7. Server Structure

A server application will internally look like
Pseudocode Where discussed
prepare for discovery Discovering a lookup service
discover a lookup service Discovering a lookup service
create information about a service Entry objects
export a service Service registration
renew leasing periodically Leasing

The following code is simplified from the real case, by omitting various checks on exceptions and other conditions. It exports an implementation of a file classifier service, as a FileClassifierImpl object. The full version is given in a later chapter. We don't give detailed explanations right now, this is just to show how the above schema translates into actual code.

 package nonworking;


public class FileClassifierServer implements DiscoveryListener {
    
    protected LeaseRenewalManager leaseManager = new LeaseRenewalManager();

    public static void main(String argv[]) {
	new FileClassifierServer();

        // keep server running (almost) forever to 
	// - allow time for locator discovery and
	// - keep re-registering the lease
        Thread.currentThread().sleep(Lease.FOREVER);

    }

    public FileClassifierServer() {
	LookupDiscovery discover = null;

        // Prepare for discovery - empty here

        // Discover a lookup service
        // This uses the asynchronous multicast protocol,
        // which calls back into the discovered() method
        discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);

        discover.addDiscoveryListener(this);
    }
    
    public void discovered(DiscoveryEvent evt) {
        ServiceRegistrar registrar = evt.getRegistrars()[0];
        // At this point we have discovered a lookup service

        // Create information about a service
	ServiceItem item = new ServiceItem(null,
         				   new FileClassifierImpl(), 
					   null);

        // Export a service
	ServiceRegistration reg = registrar.register(item, Lease.FOREVER);

	// Renew leasing
	leaseManager.renewUntil(reg.getLease(), Lease.FOREVER, this);
    }
} // FileClassifierServer
 

1.8. Partitioning an Application

Jini uses a ``service'' view of applications. This is in contrast to the simple object-oriented view of an application. Of course, a Jini ``application'' will be made up of objects, but these will be distributed out into individual services, which will communicate via their proxy objects. The Jini specification claims that in many monolithic applications there are one or more services waiting to be released, and making them into services increases their possible uses.

To see this, we can look at a ``smart file viewer''. This is an application that will be given a filename, and based on the structure of the name will decide what type of file it is (.rtf is Rich Text Format, .gif is a GIF file, and so on). Using this classification it will then call up an appropriate viewer for that type of file, such as an image viewer or document viewer. A UML class diagram for this might look like figure 1.10

Figure 1.10: UML diagram for an application

There are a number of services in this. Classifying a file into types is one (which will be used heavily in the sequel - because it is simple). This service can be used in lots of different situations, not just viewing contents. Each of the different viewer classes is another. This is not to say that every class should become a service! That would be overkill. What makes these qualify as services is that they

  1. have a simple interface

  2. are useful in more than one situation

  3. can be replaced or varied

They are reusable, and this is makes them good candidates for services. They do not require high-bandwidth communication, and are not completely trivial.

If the application is re-organised as a collection of services, then it could look like figure 1.11

Figure 1.11: Application as a collection of services
Each service may be running on a different machine on the network (or on the same machine - it doesn't matter). Each exports a proxy to whatever service locators are running. The SmartViewer application finds and downloads whatever services it needs, as it needs them.

1.9. Support Services

The three components of a Jini system are clients, services and service locators, which may run anywhere on the network. These will be implemented using Java code running in Java Virtual Machines (JVM). The implementation may be in pure Java but it could make use of native code by JNI (Java Native Interface) or make external calls to other applications. Often, each of these applications will run in its own JVM on its own computer, though they could run on the same machine or even share the same JVM. When they run, they will need access to Java class files, just like any other Java application. Each component will use the CLASSPATH environment variable or use the classpath option to the runtime to locate the classes it needs to run.

However, Jini also relies heavily on the ability to move objects across the network, from one JVM to another. In order to do this, particular implementations must make use of support services such as RMI daemons and HTTP (or other) servers. The particular support services required depend on implementation details, and so may vary from one Jini component to another.

1.9.1 HTTP server

A Java object running as a service has a proxy component exported to the service locators and then onto a client. It passes through one JVM in ``passive'' form and is activated ( brought to life) in the client's JVM. Essentially, a ``snapshot'' of the object's state is taken using serialization, and this snaphot is moved around. An object consists of both code and data, and it cannot be reconstituted from just its data - the code is also required. Where is the code? This is where a distributed Jini application differs from an ordinary one: the code is not likely to be on the client side. If it was required to be on the client side, then Jini would lose almost all of its flexibility because it wouldn't be possible to just add new devices and their code to a network. The class definitions are most likely on the server, or perhaps on the vendor's home Web site.

So class definitions for service proxy objects must also be downloaded, usually from where the service came from. This could be done using a variety of methods, but most commonly an HTTP or FTP protocol is used. The service specifies the protocol used and also the location of the class files using the java.rmi.server.codebase property. The object's serialized data contains this codebase, which is used by the client to access the class files.

If the codebase specifies an HTTP url, then there must be an HTTP server running at that url, and the class files must be there also. This often means one HTTP server per service, but this isn't required: a set of services could make their class files available from a single HTTP server, and this server could be running on a different machine to the services. This gives two sets of class files: the set needed to run the service (specified by CLASSPATH) and the set needed to reconstitute objects at the client (specified by the codebase property). For example, the mahalo service supplied by Sun as a transaction manager uses the class files in mahalo.jar to run the service, and the class files in mahalo-dl.jar to reconstitute the transaction manager proxy at the client. These files and support services are shown in figure 1.12

Figure 1.12: Support services for mahalo

To run mahalo, the CLASSPATH must include mahalo.jar, and to reconstitute its proxy on a client, the codebase property must be set to mahalo-dl.jar.

1.9.2 RMI daemon

A proxy service gets exported to the client. In most cases this will need to communicate back to its host service. There are many ways to do this. One mechanism is the Java Remote Method Invocation (RMI) system. This comes in two flavours in JDK 1.2: the original UnicastRemoteObject and the newer Activable class. Whereas UnicastRemoteObject requires a process to remain alive and running, Activable objects can be stored in a passive state and the Activation system will create a new JVM if needed when a method call is made on the object. While passive, an activatable object will need to be stored on some server, and this server must be one which can accept method calls and activate the objects. Sun supply such a server, called rmid.

This is real, real obscure and deep stuff if you are new to RMI or even to the changes it is going through. So why is it needed? Sun supply a service locator called reggie, and this is really just another Jini service that plays a special role. In particular, it exports proxy objects - the registrar objects. What makes this complex is that reggie uses Activable in its implementation. In order to run reggie, you first have to start an rmid server on the same machine, and then reggie will register with it.

This isn't all downhill: running rmid has beneficial side-effects. It maintains log files of its own state, which includes the activable objects it is storing. So reggie can crash or terminate, and rmid will restore it as needed. Indeed, even rmid can crash or be terminated, and it will use its log files to restore state so that it can still accept calls for reggie objects.

1.10. Summary

A Jini system is made up of three parts

  1. Service

  2. Client

  3. Service locator

Code is moved between these applications. A registrar acts as a proxy to the lookup locator, and runs on both the client and service.

A service and a client both possess a certain structure, which is detailed in the following chapters. Services may require support from other non-Jini servers, such as an HTTp server.

1.11. Copyright

If you found this chapter of value, the full book is available from APress or Amazon . There is a review of the book at Java Zone . The current edition of the book does not yet deal with Jini 2.0, but the next edition will.

This file is Copyright (©) 1999, 2000, 2001, 2003, 2004 by Jan Newmarch (http://jan.netcomp.edu.au) jan@newmarch.name.

Creative Commons License This work is licensed under a Creative Commons License, the replacement for the earlier Open Content License.