| ★ wanayoo — archive 1999 http://developer.java.sun.com/developer/technicalArticles/jini/userinterface/index.html | Nouvelle recherche | Portail wanayoo |
|
|
|
Articles
Index
This article gives you a glimpse of the JiniTM 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.
How to Attach a User Interface to a Jini ServiceBy Bill Venners;Reprinted from JavaWorldDiscussing Service UIs on the Jini-users Mailing ListMy experience with Jini service UIs began back in January, when I posted the following query to the Jini-users mailing list (see Resources):
The first reply to appear on the list was from Brian Jeltima of Sun Microsystems:
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:
(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:
Discussing Service UI at the Jini Community SummitAt 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
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 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:
Creating the Serviceui ProjectI 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 ServiceAs a general rule, you should not make a Jini service object extend a class from a user interface library, such asPanel 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.
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:
Step 1. Define a Factory ObjectTo 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 InterfaceThe 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'sgetUI() method may throw the following
two exceptions: InvalidServiceClassException
andUICreationException. 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 ClassTo add a UI to a service, you must create a concrete class that implements theUIFactory 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 UIYour factory (shown above) instantiates and returns a UI object, in this case, aJPanel. 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 EntryLastly, 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, likeUIFactoryEntry, 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 SwingJPanel 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
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
If such a resource-constrained client wants to display a UI for a service it
got from calling
For this reason, services that want to be friendly to resource-constrained
clients should implement the
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 CasesA Resource-Constrained ClientTo find an appropriate UI for a service, a resource-constrained client would:
A Resource-Rich ClientTo find an appropriate UI for a service, a resource-rich client could:
DiscussionTo 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 JiniTM 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 CconnectionSM) JDK 1.2: Download page for JDKTM 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 Article: An article describing scenarios showing how Jini might be used in the real world Jini: The main JiniTM 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 listserv@java.sun.com: Subscribe to the JINI-USERS mailing list by typing, subscribe jini-users, in the body of the message
List Archives: JINI-USERS mailing list archives
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 AuthorBill 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). Reader FeedbackTell us what you think of this article.
|