How to Attach a User Interface to a Jini Service

   
   

Articles Index | Jini Index | Wireless Initiative


This article gives you a glimpse of the Jini community in action and looks at what the serviceui project from Jini.org has proposed for a standard way to attach a user interface to a Jini service.

Discussing Service UIs on the Jini-users Mailing List

My experience with Jini service UIs began back in January, when I posted the following query to the Jini-users mailing list (see Resources):

 

"Where will a user interface usually reside in service items? The service object itself could be an applet, the service item could have methods that put up the UI, or you could attach one or more applets as attributes.

If a service has a UI that must be used every time the service is used, would you prefer to make the service object itself an applet, keep the UI separate from the service object and enter all applets as attributes, or invoke a method that puts up a UI?

Or, I suppose, will there be a preference? It seems the "community" will need to develop guidelines on how you should architect service items, and how clients should behave when presented with different types of service objects and service items.

Any ideas? bv"

The first reply to appear on the list was from Brian Jeltima of Sun Microsystems:

"In my opinion, the GUI should rarely (I'd like to say never) be an integral part of the service object. I think of the service as a protocol defined via interfaces. A default user or management GUI can be attached as an attribute, but the user of the service is free to interact with the protocol directly and avoid the download of these bundled GUI objects if they are not needed.

I imagine that a preferred approach will evolve rather quickly as device manufacturers will be burning their solutions into ROM. The difficulty of changing code in consumer devices will prompt them to think through these issues early on, I would hope.

Brian"

Brian's post was followed by 16 others discussing how to attach a UI to a service. I read all of these posts and then tried to summarize them on the Jini-users FAQ as an answer to the question: Where in the service item should I put the UI for my service? Here's a snippet from that answer, which describes how I think a UI object would get a reference to the Jini service object:

 

"Your factory instantiates and returns a UI object. The [UI object] you write must accept a reference to the service object in its constructor. This reference will be stored in a local variable, so the [UI object] can use it. The [UI object] serves as an intermediary between the user and the service."


(For a complete record of this first attempt to show how to add a UI to a service, see the answer to question 14 on an early version of the Jini-users FAQ.)

In response to my service UI answer, Jeltima made the following comment, which describes his concern about my recommendation that a service object reference be passed to the UI object constructor:

 

"The problem with passing the service object in the constructor is that if the implementor decides to actually provide the GUI as a serialized object in an attribute, then, when the client deserializes the GUI, the constructor won't be called.... So I think [passing a service object reference to] a separate init() function [of the UI object] is a bit more general [than passing a service object reference to the UI object's constructor.]"


I thought Brian's comment made sense, but as I was very busy with other things, my FAQ answer remained relatively unchanged for the next several months.

Discussing Service UI at the Jini Community Summit



At the Jini summit last May in Aspen, CO, however, the issue of attaching a UI to a service was revisited. During his presentation called "Creating and Building New Jini Services," Jon Bostrom of Sun said that it was important that someone define a standard way of attaching a UI to a service.

Most people at the presentation agreed. I also voiced my agreement and described January's Jini-users discussion and the resulting FAQ answer. I also mentioned Jeltima's preference of using an init() method rather than a constructor to pass a service object reference to a UI object. I said, "If I do it my way and Brian does it his way, then clients will have to know about both ways to offer UIs to each of our Jini services." I also added that if no standard way of attaching a UI to a service became generally accepted, service providers would attach UIs independently and clients would need to know 500 different ways to look for a service UI.

Unfortunately, I discovered as I was writing this article that the example I gave at the Jini summit was wrong. As I myself had noted in the first FAQ proposal, the UI object was being created and returned by a factory method. A factory method is meant to hide from the client the way an object (in this case, the UI object) is instantiated. Because the factory method is responsible for initializing the new UI object, only the factory method — not the client — needs to worry about whether to pass a service object reference to a constructor, an init() method, or some other method. Nevertheless, the attendees of Bostrom's meeting seemed to accept my conclusion that even minor differences in the way UIs are attached to services can complicate programming Jini clients.

In the end, we all agreed that defining a standard made sense, and that the Jini community was the one to tackle the problem. Bostrom said that while he'd be happy to have his team work on the approach, he thought it would be best if someone who was not from Sun started it.

Later that day, another Sun employee slipped me this note:

 

"Bill, On Jini.org, we can now create "projects." This may be a possible forum for the GUI working group that we were all discussing in the "Jini Services" breakout session."

A few days later, Ken Arnold of Sun posted this message to the Jini-users mailing list:

"We have added a new feature to the Jini.org Web site — you can create collaborative projects for shared development of Jini service definitions, development utilities, clients, documentation, and so forth.

Suppose you want to work on the correct design for GUI attributes (a popular discussion among Jini users). You can go to the projects page on Jini.org and see if someone is already working on it. If not, you can create a new project...."

Ken

Creating the Serviceui Project

I must have subconsciously taken Bostrom's and Arnold's comments as hints, because soon after this message was posted, I created a project on Jini.org called serviceui. I did this partly because the message that Sun wanted someone external to start the project had been firmly imprinted on my brain, partly because a personal experiment in chaos — I mean, democracy — seemed intriguing, and partly because it was so fast and easy to create a project at Jini.org that I didn't think much about it before creating it.

Shortly after creating the serviceui project, however, I had a minor panic attack. What business did I have starting this project? Shouldn't I have at least posted a query to the Jini-users list to build a consensus before acting like some trigger-happy cowboy? Time, however, calmed my anxieties. Over the next few weeks, many people joined the project and an interesting and fruitful discussion commenced on the serviceui mailing list.

So far, the project felt like a group of engineers sitting in a virtual conference room, discussing a design problem, and homing in on a solution. I have served as scribe, attempting to capture the consensus in writing. (Our current proposal forms the bulk of this article.)

I believe that in general the serviceui experiment has been successful. Our main problem has simply been that because no one is getting paid and we are all busy, our work has gone slowly. Nevertheless, we have made quite a bit of progress. Hopefully, our work will be timely enough to do some good. One of your most important goals when trying to establish a de facto standard is to define it as early as possible, before people invest too heavily in other approaches.

I hope to get a solid proposal by the next Jini summit, to be held in Annapolis, MD, in October (see Resources.) Someone from the project, perhaps myself, will present the proposal at the summit.

I'll now give you the serviceui project's current working proposal. Any feedback you have is welcome. You can submit comments to my usual discussion forum at Artima.com. Or you can join the serviceui project at Jini.org and post comments to its mailing list.

How to Add a UI to a Jini Service



As a general rule, you should not make a Jini service object extend a class from a user interface library, such as Panel or JPanel.. Think of the service object as the way clients interact with a service through the well-known Java interfaces that the service object implements. To associate a UI with a service, use attributes.

You can associate many UIs with a service by placing entries for the UIs in the service item's attributes. One reason to keep the UI separate from the service object is that services may be used in devices that don't have user interface libraries available. If you attach a UI to a service item as attributes, those clients who have user interface libraries can retrieve your UI and display it. Clients who don't have them can just use your service object directly.

Each UI is represented by one UI object, which may or may not represent a graphical UI component. A UI object could represent a voice-only interface, a text interface, a combination of voice and graphics, a 3D-immersible world, and so on. Each distinct kind of UI, including a 3D-immersible world with voice and a virtual-text display, would be represented in the attributes by one UI object.

Figure 1. Client code talks to a service through the Jini interface

Think of a service UI object as a type of "human adapter" — an object that sits between the Java interface offered by the service object and a human who wants to use the service. Often a client program looks up a Jini service by Jini interface type. As shown in Figure 1, such client programs can use the service object directly by invoking methods in the service object's Jini interface. The service object's Java interface must provide access to all the service's functionality. Thus, a UI object merely serves as a go-between. As shown in Figure 2, a UI object gives a human user access to the service functionality via the Jini service interface.

The current working proposal gives four steps for adding a UI to a service. The user will:

  1. Define a factory object
  2. Pass the service object to the UI
  3. Place the UIFactory in an Entry
  4. Have the service object implement Identifiable (optional)

Step 1. Define a Factory Object



To add a UI as an attribute, create a factory object with a method that instantiates and returns a new UI object. This approach lets you make UI objects available without requiring that they get serialized and stored in the lookup service. You only need to serialize and store the factory-object attribute and its enclosing entry in the service.

The UIFactory Interface



The factory object will need to implement a well-known UI factory interface (and the serviceui project should propose one). Here's an example of what the interface might look like:

// In file UIFactory.java
package org.jini.ui;

public interface UIFactory extends 
              java.io.Serializable {

   Object getUI(Object serviceObject)
        throws UICreationException;
}

Note that the package prefix org.jini.ui is used here to represent whatever package prefix the community eventually decides upon. Please do not use this package prefix. The org.jini package space is controlled by the Jini community, which hasn't given anyone permission to use org.jini.ui.

Exceptions Thrown by getUI()



A factory object's getUI() method may throw the following two exceptions: InvalidServiceClassException and UICreationException. The unchecked InvalidServiceClassException will be thrown by getUI() if it cannot cast the passed reference to the expected service object class.

// In file InvalidServiceClassException.java
package org.jini.ui;

public class InvalidServiceClassException 
                  extends RuntimeException {

    public InvalidServiceClassException() {
    }

    public InvalidServiceClassException(
                              String msg) {
        super(msg);
    }
}

The checked UICreationException will be thrown by getUI() if the factory cannot produce the UI object. If an exception thrown by the UI object's constructor detects this problem, a reference to that exception should be passed to the constructor of UICreationException. This enables clients to retrieve the original exception by invoking getThrownException() on the caught UICreationException.

// In file UICreationException.java
package org.jini.ui;

public class UICreationException 
                extends Exception {

    private Exception thrownException;

    public UICreationException() {
    }

    public UICreationException(
                       String msg) {
        super(msg);
    }

    public UICreationException(
                      Exception e) {
        thrownException = e;
    }

    public Exception getThrownException() {
        return thrownException;
    }
}

2: Defining a Concrete Factory Class



To add a UI to a service, you must create a concrete class that implements the UIFactory interface, such as:

/*
 * PrinterJPanelFactory - 
 * An implementation of the UIFactory 
 * interface that knows to cast the service
 * object to type PrinterService
 * and instantiates and returns a 
 * new PrinterJPanel GUI object.
*/
package com.artima.printer;

import org.jini.ui.UIFactory;
import org.jini.ui.InvalidServiceClassException;
import org.jini.ui.UICreationException;
import org.printerfolks.PrinterService;

public class PrinterJPanelFactory 
                 implements UIFactory {

    public Object getUI(
                  Object serviceObject)
        throws UICreationException {

        PrinterService ps;
        try {
            ps = (PrinterService) 
                         serviceObject;
        }
        catch (ClassCastException e) {
            throw new 
             InvalidServiceClassException();
        }

        try {
            return new PrinterJPanel(ps);
        }
        catch (Exception e) {
            throw new UICreationException(e);
        }
    }
}

Each UI factory class knows the type of service object that should be passed to its getUI() method. The first thing every getUI() method should do is attempt to cast the passed object to the expected type. If the cast doesn't succeed, the client has passed in an inappropriate object and the method should throw the unchecked InvalidServiceClassException. Otherwise, the getUI() method should attempt to create the UI object, passing the reference to the service object to the UI object's constructor. If any exception is thrown, the getUI() method should create and throw a new UICreationException, encapsulating the original exception inside the UICreationException.

Step 2. Pass the Service Object to the UI



Your factory (shown above) instantiates and returns a UI object, in this case, a JPanel. The JPanel you write must accept a reference to the service object in its constructor. The reference will be stored in an instance variable of the JPanel object, so that the JPanel can interact with the service. The JPanel can then serve as an intermediary between the user and the service. Here's an example:

/*
 * PrinterJPanel - A GUI panel that 
 * allows the user to interact with
 * a printer across the network via a 
 * Jini service object
*/
package com.artima.printer;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.accessibility.*;
import java.awt.*;
import java.awt.event.*;
import org.jini.ui.*;
import org.printerfolks.*;

public class PrinterJPanel 
                     extends JPanel {

  private PrinterService serviceObject;

  //...

    public PrinterJPanel(
        PrinterService serviceObject) {

      this.serviceObject = serviceObject;

        //...
    }

    //...
}

Step 3. Place the UIFactory in an Entry



Lastly, you must place your factory object in an entry so that it can be attached to the service item. Eventually, you should be able to use well-known entry classes to hold your UI factory attributes. All UI factory entry classes will probably belong to a family of classes rooted on a class, like UIFactoryEntry, shown here:

// In file UIFactoryEntry.java
package org.jini.ui.entry;
import net.jini.entry.AbstractEntry;
import java.util.Map;

public class UIFactoryEntry 
     extends AbstractEntry {

    public String name;
    public String function;
    public UIFactory factory;
    public Map properties;

    UIFactoryEntry() {
    }

    UIFactoryEntry(String name, 
       String function, UIFactory factory,
        Map properties) {

        this.name = name;
        this.function = function;
        this.factory = factory;
        this.properties = properties;
    }
}

The name variable is a string that should be unique among the UIFactoryEntry objects associated with each service item. You use the name when searching for an appropriate UI from among the UIs associated with a single service, as described later in this proposal.

The function variable indicates the UI's role. Some services may have different UIs for different purposes and give them different function names that indicate the purpose. Some examples are main for the main UI, admin for an administration UI, and so on. The serviceui project should establish recommended names for common functions and set guidelines for service providers who want to use different function names.

The factory variable simply holds a reference to the UI factory object that will produce the UI object.

The properties variable holds a Map that enables service providers to associate with a UI (property objects) that describes the UI. Clients can search through these properties to help them locate the most appropriate UI object for their needs. A description of the process is provided later in this proposal.

The inheritance relationships of UIFactoryEntry subclasses will correspond to the inheritance relationships of the UI objects they generate. For example, given that java.awt.Panel subclasses java.awt.Component, the corresponding UI factory entries would have a similar relationship. For example, PanelFactoryEntry would subclass ComponentFactoryEntry. Here's how that might look:

// In file ComponentFactoryEntry.java
package org.jini.ui.entry.java.awt;
import org.jini.ui.UIFactoryEntry;
import java.util.Map;

public class ComponentFactoryEntry 
              extends UIFactoryEntry {

    // Could add other public fields 
    // here that allow instances to
    // characterize themselves in a way 
    // that can be selected by
    // matching criteria in the 
    // ServiceTemplate passed to an
    // invocation of lookup() on a 
    // ServiceRegistrar.

    ComponentFactoryEntry() {
    }

    ComponentFactoryEntry(String name, 
       String function, UIFactory factory,
        Map properties) {

        super(
         name, function, factory, properties);
    }
}

// In file PanelFactoryEntry.java
package org.jini.ui.entry.java.awt;
import java.util.Map;

public class PanelFactoryEntry 
       extends ComponentFactoryEntry {

    // Could add other public fields here 
    // that allow instances to
    // characterize themselves in a way 
    // that can be selected by
    // matching criteria in the ServiceTemplate 
    // passed to an invocation of lookup() on a
    // ServiceRegistrar.

    PanelFactoryEntry() {
    }

    PanelFactoryEntry(
      String name, String function, 
                     UIFactory factory,
        Map properties) {

        super(name, function, factory, 
                           properties);
    }
}


To avoid name conflicts, the package names of factory entry classes should be composed of a to-be-chosen UI entry-package prefix (here, org.jini.ui.entry), followed by a dot and the package name of the UI object class that the factory generates. Thus, the package name for a factory entry for javax.swing.JPanel would be org.jini.ui.entry.javax.swing. This package-naming scheme enables anyone to easily and immediately add a factory entry for a newly invented UI object to the well-known UI entry package (here, org.jini.ui.entry). You shouldn't worry that you will interfere with someone else who happened to pick the same name, nor should you have to apply to some committee that controls the namespace for UI factory entries. If IBM invents a new UI object that combines voice and graphics and gives it the fully qualified name com.ibm.ui.vng.VoiceAndGraphics, it could immediately name its UI factory entry VoiceAndGraphicsFactoryEntry and place it in the org.jini.ui.com.ibm.ui.vng package.

The name field of each UIFactoryEntry associated with a particular service should be unique. (In other words, the uniqueness of these names need only be enforced among the attributes of each individual service item.) The name field lets a client grab the UIFactory object from a specific UIFactoryEntry via the getFieldValues() method of the ServiceRegistrar. You'll see an example of this later.

The function field of the JPanelFactoryEntry lets different UI objects that serve different purposes be identified in an attribute search. Possible names for functions are main and admin. The serviceui project should define a set of standard function strings.

The properties field contains a mapping from strings to property objects that characterize the UI object. For example, you might define a UI property object that describes the minimum screen size on which a particular UI should be used:

// In file MinimumScreenSize.java
package org.jini.ui;

public class MinimumScreenSize 
    implements java.io.Serializable {

    private java.awt.Dimension minScreenSize;

    public MinimumScreenSize(
             java.awt.Dimension dim) {

        this.minScreenSize = dim;
    }

    public java.awt.Dimension 
            getMinimumScreenSize() {
        return minScreenSize;
    }
 }


A MinimumScreenSize property could then be associated with a UI object by adding it to the properties map of the UI's entry object and associating it with a well-known key, such as minscreensize. Clients could then inspect this property and others by searching in the map by their well-known keys. The UI's name and function are two properties that should sit in every map. (In other words, the information stored in the name and function fields of the UIFactoryEntry object should also appear in the properties map, perhaps under the keys name and function.) Properties, such as MinimumScreenSize, give information about particular UIs to facilitate client searches for the best UI for a situation. (Note that because of name and function, all entries should have a properties map.)

Step 4. Have the Service Object Implement Identifiable

(optional) Service providers can attach any number of UIs to a service object by including them in the service item as attributes. A service provider may want to offer multiple UIs to accommodate clients who have different capabilities and users who have different preferences. A service could attach a Swing JPanel geared toward users sitting on a desktop, an AWT Panel aimed at television screens and remote-control input devices, a different AWT Panel for small PDAs with touch-sensitive screens, a UI object that offers a voice-only user interface for telephone users, and so on. The client must be able to search through the service's UI options easily and quickly to find the best service that matches its capabilities and its user's preferences.

To search for an appropriate UI object, a client with sufficient resources can just use the ServiceMatches lookup(ServiceTemplate, int) method of the ServiceRegistrar to retrieve the entire attribute set of a service along with the service object and its ServiceID object. It can then search through its local copies of all the attributes to find an appropriate UI.

Many clients, however, will not want to download all the attributes. Those attributes take up memory, which can be a scarce resource in a small consumer device. In addition, downloading them may trigger unnecessary class files to be downloaded as well. (The JVM specification lets implementations load classes before they are used.) Such clients can use the Object lookup(ServiceTemplate) method of ServiceRegistrar, which, rather than returning an array of matching ServiceItems, returns only one matching service object. If more than one service matches the template passed to this lookup() version, one is selected arbitrarily and returned.

If such a resource-constrained client wants to display a UI for a service it got from calling Object lookup(ServiceTemplate), it must search through that service's attributes via methods declared in the ServiceRegistrar interface. Unfortunately, the client won't have enough information to uniquely identify the service whose object it has a local copy of. To be certain it can uniquely identify the service, the client needs the service's ServiceID. But when the client invokes lookup(), it just gets the service object, not the ServiceID. Thus, the only way this client can get the ServiceID is by asking the service and invoking a method on the service object.

For this reason, services that want to be friendly to resource-constrained clients should implement the Identifiable interface, which lets clients ask the service for its ServiceID:

package org.jini.ui;
import net.jini.core.lookup.ServiceID;

public interface Identifiable {

    ServiceID getServiceID();
}

Note: As an alternative to Identifiable, it might be a nice enhancement to the ServiceRegistrar interface to add a method that returns one service object and its associated ServiceID, but no attributes.

Use Cases



 

A Resource-Constrained Client



To find an appropriate UI for a service, a resource-constrained client would:

  1. Get a service object
  2. Client builds a ServiceTemplate that describes the service it desires, and invokes Object lookup(ServiceTemplate) on a ServiceRegistrar. The ServiceTemplate can include one or more UIFactoryEntry objects; these help the search to just the requested services that offer one or more UIs the client may potentially use.

     

  3. Get the service ID
  4. The client makes sure the service object implements Identifiable with instanceof. If it does, the client casts the service object reference to Identifiable, and invokes getServiceID() on that reference. The client now has the service ID.

    Note that if the service object does not implement Identifiable, the client has to use the other form of lookup() to get an entire ServiceItem. This consumes more resources, but it yields a local copy of all attributes, which enables the client to search locally for an appropriate UI. Using this alternative approach may not be practical (or possible) for a resource-constrained client, which is why providers of services that these clients can be expected to request should implement Identifiable.

     

  5. Retrieve property maps for candidate UIs
  6. The client builds a ServiceTemplate composed of the ServiceID object, which uniquely identifies the service, and an instance of a subclass of UIFactoryEntry, which indicates the desired UI type and potentially some desired UI characteristics. (For example, if the client is interested in a java.awt.Panel, it would create an instance of PanelFactoryEntry and put that in the ServiceTemplate.)

    Once the client has the ServiceTemplate, it invokes the getFieldValues(ServiceTemplate tmpl, int setIndex, String field) method on the ServiceRegistrar. In the tmpl argument, it passes a reference to the ServiceTemplate. In the setIndex argument, it passes zero (or whatever index the UIFactoryEntry exists in the ServiceTemplate's attr field). In the field argument, it passes the string properties.

    The client gets an array of Map objects as the return value of the getFieldValues() method invocation. Each Map object contains properties that characterize a UI whose entry matched the type specified in the ServiceTemplate. The client can then use the property maps to select an appropriate UI.

    For example, if the client has a small screen size, it may look for a minscreensize property in each Map, then compare the minimum screen size properties to find the UI object that is most suitable for the client's display capabilities. Other properties can include locales that the UI directly supports, so a client with a French-speaking user can find UIs that offer a French option. Any useful way to characterize a UI can be given a well-known property name and added to the Maps that sit in the UIFactoryEntry objects.

     

  7. Retrieve one UIFactory
  8. When the client decides which Map represents the best UI for its needs, it looks up the name property on that Map. The name property should contain a String with an identical value to the String referenced by the name field of the corresponding UIFactoryEntry subclass. For example, if the name field of a JPanelFactoryEntry refers to a String with the value Bob's Groovy UI, then you should get a String with the value Bob's Groovy UI when you look up the name property on the Map contained in the properties field of that same JPanelFactoryEntry.

    Having retrieved the name property from the selected Map, the client builds a new ServiceTemplate composed of the ServiceID object and an instance of UIFactoryEntry with the name field set to the retrieved value of the name property. Then, the client once again invokes getFieldValues() on the ServiceRegistrar. It passes in its new ServiceTemplate in the tmpl argument, the appropriate index (probably zero) in the setIndex argument, and the String factory in the field argument.

    The name field of each UI factory-entry object in a particular service item's attributes array should be unique. Thus, the ServiceTemplate passed to the most recent invocation of getFieldValues() should match only the one UI factory entry that has the requested name. Therefore, getFieldValues() will return one UIFactory object. This object generates the UI object the client deemed to be most appropriate given its capabilities and user's preferences.

     

  9. Create the UI
  10. Lastly, the client simply invokes getUI() on the UIFactory, passing in a reference to the service object. The getUI() method returns a new UI object. Although the type of reference is an object, the client can downcast that reference to a more specific type. If the client is looking for a UI among PanelFactoryEntrys, for example, it can downcast the reference returned by the UIFactory to java.awt.Panel.

 

A Resource-Rich Client



To find an appropriate UI for a service, a resource-rich client could:

  1. Get a set of service items

    Client builds a ServiceTemplate that describes the service it desires, then invokes Object lookup(ServiceTemplate, int) on a ServiceRegistrar. The ServiceTemplate can include one or more UIFactoryEntry objects to help narrow the search to just the requested services that offer one or more UIs that the client may potentially use.

  2.  

  3. Search for an appropriate service and UI

    This resource-rich client already has a set of matches for its query that includes full information about matching services. The client can simply search through this information to find an appropriate service and UI.

  4. Create the UI

    Lastly, the client simply invokes getUI() on the chosen UIFactory, passing in a reference to the chosen service object.

 

Discussion



To discuss the material presented in this article, including submitting feedback about the serviceui proposal, visit the discussion forum at Serviceui. The small print: "How to attach a UI to a service" article Copyright © 1999 Bill Venners. All rights reserved.

RESOURCES:



Serviceui: To comment about the current serviceui proposal, post at this forum:
Jini.org: Participate in the serviceui project
Community Meeting: For more information about the next Jini summit:
Book List: Recommended Jini technology books:
FAQ: A Jini FAQ for the JINI-USERS mailing list:
Resources: Links to Jini technology resources:
Releases: Download page for the current Jini release (at the Java Developer Cconnection )
JDK 1.2: Download page for JDK 1.2 FCS Release, on which the current Jini release runs
Tutorial: An online Jini technology tutorial
Notes: Online lecture notes for a course about RMI and Jini technology
Jini: The main Jini page at Sun Microsystems.
Jini.org: The Jini Community, the central site for signers of the Jini Sun Community Source License to Interact
Jini Specifications: Download page for all of the Jini specifications

Reprinted with permission from the October 1999 edition of JavaWorld magazine. Copyright Web Publishing Inc., an IDG Communications company. Register for editorial e-mailalerts

 

About the Author



 

Bill Venners has been writing software professionally for 14 years. Based in Silicon Valley, he provides software consulting and training services under the Artima Software Company. He has developed software for the consumer electronics, education, semiconductor, and life insurance industries. Bill has programmed in many languages on many platforms: assembly language on various microprocessors, C on Unix, C++ on Windows, Java on the Web. He wrote Inside the Java 2.0 Virtual Machine, (Enterprise Computing, October 1999).