Here are some questions frequently posted to the JINI-USERS mailing list:
com.sun.rmi.rmid.ExecOptionPermission
exceptions when
starting Jini?
Jini is a set of APIs and runtime conventions that facilitate the building and deploying of distributed systems. Jini provides "plumbing" that takes care of common but difficult parts of distributed systems.
Jini consists of a programming model and a runtime infrastructure. By defining APIs and conventions that support leasing, distributed events, and distributed transactions, the programming model helps developers build distributed systems that are reliable, even though the underlying network is unreliable. The runtime infrastructure, which consists of network protocols and APIs that implement them, makes it easy to add, locate, access, and remove devices and services on the network.
For a detailed introduction to Jini, see:
http://www.artima.com/jiniology/intro.html
What does "Jini" stand for?
Jini isn't an acronym, so it doesn't stand for anything. Though, as
Ken Arnold pointed out, it does function as an anti-acronym:
"Jini Is Not Initials." It is pronounced the same as the word "genie,"
so it sounds like jee-nee, not jin-nee.
Where will Jini be useful?
Jini is intended to be of general use, applicable in a wide range of distributed systems environments. In the home, it can help simplify the job of "systems administration" sufficiently that non-sophisticated home owners will be able to compose and use a network of consumer devices. In the enterprise, it can make it easier for traditional systems administrators to manage a dynamic network environment.
Thus, the home and the enterprise are two major network environments where
Jini may make sense, but Jini's potential is not limited to these
environments. Basically, anywhere where there is a network, Jini could
potentially provide the plumbing for the distributed systems running on
that network. For example, networks are emerging in cars, which nowadays
contain many embedded microprocessors. Were you to plug a
new CD player into your car, Jini could, for example, enable the user
interface for the CD player to come up on your car's dashboard.
How is Jini licensed?
Sun licenses the Jini source code through the "Sun Community Source Code License," or "SCSL," which is a cross between keeping things proprietary and giving everything away via an open source license. For more information, see:
Also, check out The Jini Community, the central site for signers of the Jini Sun Community Source Licence to interact:
http://www.jini.org
Will Jini work with PersonalJava?
Currently Jini does not work with PersonalJava, because (among
other reasons) Jini depends on 1.2 RMI, and the current release of
PersonalJava doesn't support 1.2 RMI. Sun says they expect to release
later this year a new version of PersonalJava that supports Jini.
Do clients need a priori knowledge to use a service?
To use a service, clients will in general have a priori knowledge of at least one "known" (or "well-known") Java interface that the service object implements. In other words, if a client program makes use of, say, a Jini storage service, the programmer of the client will usually know about a standard (or, "well-known") interface to storage services when the programmer writes the client program.
Clients will usually specify one or more Java interfaces as part of the search criteria when looking for services on a lookup service. This approach means that clients will definitely know how to use the services they find, because by definition they know (have a priori knowledge of) at least one Java interface through which they can interact with the service.
Currently, no "well-known" interfaces really exist. These interfaces will likely be defined by industry consortiums. For example, various manufacturers of printers have gotten together to define a hierarchy of interfaces for printing. As families of "well-known" interfaces come into the public domain, they will likely be described and made available at the Jini Community web site:
Currently, two working groups are forming to define "well-known" interfaces for printing and storage services. For information about these and other efforts, visit the Jini Community web site.
Although in the general case, client programs will have a priori knowledge of well-known interfaces implemented by the service objects they use, they needn't always. If a service offers a user interface (UI) the client can display, and that UI is attached to the service in a "standard" way known to the client, the client need not have any a priori knowledge about that service. The client need only have a priori knowledge of where the UI might be stored (likely as an attribute in the service item). Once the client locates the UI, it can pass a reference to the service object to the UI and display it. The user can interact directly with the UI, which interacts with the service via the service object. Because the UI knows how to use the service object, the client doesn't have to.
The serviceui project at jini.org is currently working to define a well-known way for UIs to be attached to services. To find out more, join the serviceui mailing list at:
http://developer.jini.org:80/cgi-bin/subscribeform
(You'll have to log in as a Community member, which requires that you agree
to the Sun Community Source License terms for Jini.)
What's the difference between Jini and RMI?
Jini is built on top of RMI, so Jini requires RMI. RMI does not, however, require Jini. RMI, which stands for Remote Method Invocation, enables clients to hold references to objects in other JVMs, to invoke methods on those remote objects, and to pass objects to and from those methods as parameters, return values, and exceptions. RMI's object-passing abilities include a mechanism that enables downloading of any required class files across the network to the JVM receiving the object. Jini enables the spontanteous networking of clients and services on a network. Jini makes extensive use of RMI's ability to send objects across the network.
Both Jini and RMI involve a kind of directory service. In the case of Jini, the directory is called the lookup service. In the case of RMI, it is called the registry. The Jini lookup service enables clients to get references to services, which arrive in the local JVM as a reference to a local service object that implements a service interface. Similarly, the RMI registry enables clients to get references to remote objects, which arrive in the local JVM as a reference to a local stub object that implements a remote interface. Despite these similarities, however, several significant differences exist between Jini lookup services and RMI registries.
In general, a client program requires more knowledge about the network to use RMI than Jini, because Jini provides a discovery protocol that enables clients to locate nearby lookup services without prior knowledge of their locations. RMI, by contrast, requires that a client already know where a remote object is installed. In addition, because Jini service providers can also use the discovery protocol to locate lookup services, they can make their services available to the local area network without prior knowledge of that network. Because of the discovery protocol, Jini is able to provide "spontaneous networking" or "plug and work" between devices. Because it lacks a discovery protocol, RMI is not quite so spontaneous.
Another significant difference between RMI and Jini is the difference between a service object and a stub object. Although Jini makes use of RMI to send objects around, the service object on which the client invokes methods need not be an RMI stub. The Jini service object could be an RMI stub, but it's not necessary. A Jini service object can use any network protocal, not just RMI, to communicate back to any server, hardware, or whatever may be across the network. Alternatively, a Jini service object could fully implement the service locally so that it need not do any communication across the network. In the case of RMI, stub objects will always use the RMI wire protocol to communicate across the network to the remote object. A designer of each service decides which (if any) protocol is used to communicate across the network between a service object local to a client and a remote server. This means that Jini effectively raises the level of abstraction of distributed systems programming from the network protocol level to the object interface level. Clients don't have to worry about protocols, they just talk to object interfaces.
The third significant difference between RMI and Jini is that Jini provides a much more expressive way to search for services in a lookup service than RMI provides to search for remote objects in a registry. Each remote object is associated in a registry with a character string name that is unique within that registry. Jini services are registered with a service identifier that is globally unique and permanently maintained by each service, a service object, and any number of attribute objects. Clients can look up a service by service identifier, by the Java type or types of the service object, and by wildcard and exact matching on the attributes. Whereas RMI requires that a client know the name under which a desired remote object was registered, Jini enables a client to just look for the type of service desired. The ability to search for services by Java type is another feature of Jini that supports spontaneous networking. If you enter a LAN environment for the very first time and you want to use a printer, you don't have to figure out what name a printer service was registered under; instead, you just look up the services that implement a well-known Printer interface. Lookup by type also ensures that Jini clients will know how to use whatever it is they get back from their query, because they had to know the about the type to do the query in the first place.
In summary, RMI's aim is to enable invocation of methods on objects in remote
JVMs and to enable the sending and receiving of objects between JVMs as
parameters, return values, and exceptions of those remote method calls. Jini's
aim is to enable spontaneous networking of clients and services that
happen to find themselves on a local network.
How do you find out the IP address of a Jini service?
In general, you can't obtain the IP address of a service from the
lookup service. The service proxy is never unmarshalled in the lookup
service, so it's impossible to inspect the proxy for IP addresses.
Also, there is no requirement that the entity that registers the
service must also be the entity that provides the service, so knowing
who performed the join in the general case tells you nothing. Even
after the client retrieves the service proxy, obtaining an IP address
is not possible in general, because a smart proxy can be designed to
completely hide the network implementation details from the client. In
fact, the proxy can entirely implement the service locally, resulting
in no IP address. Or, the proxy could contact several different
servers at various IP addresses simultaneously or at different periods
during its lifetime; this means that there could, in fact, be many IP
addresses. If you are a service provider and want to provide clients
with your IP address, you must explicitly give them a way to get at
it. You could either register an attribute with your service that
gives the IP address, or your service object could implement an
interface that includes a method which returns the IP address.
Where can I get the Jini release?
You can download the Jini binaries and source from the Java Developer Connection at:
http://developer.java.sun.com/developer/products/jini
You must be a member of the Java Developer Connection to access this page.
If you are not a member, you can become one for free. (Well, for
almost free. You do have to fork over some information about
yourself.)
What version of Java does the Jini release require?
As of January 26th, the Jini binaries work with the JDK 1.2 FCS release. If you downloaded the Jini binaries prior to January 26th, your Jini binaries will NOT work with JDK 1.2 FCS, but with JDK1.2b4.
Get JDK 1.2 FCS from:
http://java.sun.com/products/jdk/1.2/
Get JDK1.2b4 from the Java Developer Connection at:
http://developer.java.sun.com/developer/earlyAccess/jdk12/beta4
Jini will not work with any JDK release prior to JDK 1.2. It is built on top of
many features, such as new features of RMI, that did not exist in
the JDK prior to JDK 1.2.
Where can I get the Jini specifications?
You can download all of the Jini specifications from:
http://java.sun.com/products/jini/specs/
I'm a Jini newbie. How do I get started with Jini?
Once you download the Jini binaries and the appropriate JDK release (see above), you may wish to download some of these examples and try to get them to run:
The example code from Bill Venners' Jini/JavaSpaces talk, and instructions on getting it to compile and run, are available at:
http://www.artima.com/javaseminars/modules/Jini/CodeExamples.html
Eran Davidov posted some example code to JINI-USERS, and Ken Arnold made some modifications to get the code to run on UNIX. Eran then updated the code to work with JDK FCS 1.2. This revised version can be downloaded from:
http://www.artima.com/jini/resources/timeservice.html
Brian Murphy posted some beginner examples to JINI-USERS, which you can retrieve from the JINI-USERS archives at:
http://archives.java.sun.com/cgi-bin/wa?A2=ind9902&L=jini-users&F=&S=&P=925
A Jini tutorial is available online at:
http://pandonia.canberra.edu.au/java/jini/tutorial/Jini.xml
Online lecture notes for a course about RMI and Jini are at:
http://www.eli.sdsu.edu/courses/spring99/cs696/notes/index.html
Noel Enete's "Nuggets," which contain example code and detailed instructions, are a great way to get started running Jini:
http://www.enete.com/download/#_nuggets_
How do I lookup a service?
The ServiceRegistrar
interface provides two overloaded
lookup()
methods with which you can do lookups.
If you just want to get one service that matches the template, use
this form:
public Object lookup(ServiceTemplate tmpl) throws java.rmi.RemoteExceptionIf a service matches the template, you will receive back a reference to the service object. If no service matches the template, you'll get back a
null
reference. If more than one service matches the
template, you'll just get back one service object chosen randomly from
the matches.
If you want multiple matching service objects, or you want to know how many services total match a template, use this form:
public ServiceMatches lookup(ServiceTemplate tmpl, int maxMatches) throws java.rmi.RemoteExceptionThis method will never return null, but will always return a
ServiceMatches
object. ServiceMatches
objects have two public fields:
public int totalMatches; public ServiceItem[] items;If you just want to know how many services match a template, but don't want any service items back, invoke
lookup()
with
maxMatches
set to 0. You will get back a
ServiceMatches
object whose items
field is
null
and whose totalMatches
field indicates the
total number of services that match the template.
If you want multiple service items, just indicate your maximum in
the
maxMatches
field. You will get back a
ServiceMatches
object whose items
field contains
the array of matching service items up to your specified
maxMatches
.
The totalMatches
field will still indicate the
total number of services that match the template, which may be greater
than the length of the returned items
array.
I've got a problem. What info should I include in my post?
To help those who would help you with your problem, include the following information in your post along with the description of your problem:
As a general rule, you should not make the service object itself extend
a class from a user interface library, such as Panel
,
JPanel
, Applet
, or JApplet
. Think
of the service object as the way clients interact with a service
through the well-known Java interfaces the service object implements.
To associate UI with a service, use attributes.
One reason to keep UI out of your service object is that it may be used in devices that don't have user interface libraries available. If you attach UI to a service item as attributes, those clients that have the user interface libraries available can retrieve your UI and display it. Those clients that don't have user interface libraries available, can just use your service object directly. You can therefore associate zero to many UIs with a service by placing entries in the attributes of the service's service item.
Each UI is represented by one UI object, which can but need not represent a graphical UI component. A UI object could represent a voice only interface, a text I/O interface, a combination of visual and text, a 3D immersive world, and so on. Each one of these UIs, even a 3D immersive world with voice and a virtual text display, would be represented in the attributes by one single UI object.
A "standard" way of attaching a UI to a service is being developed by the serviceui project at jini.org. You can read about their latest ideas here:
http://www.artima.com/jini/serviceui.html
To participate in, or just keep track of, the development of a recommended
approach to adding UI to a service, including definition of well-known
interfaces and classes, join a mailing list for the serviceui project
at
jini.org
.
How can I start multiple Jini lookup services on the same
machine?
The only fixed port defined by the Jini specifications is the port for multicast discovery. Because this is a multicast port (unlike broadcast and connection-based ports), multiple lookup services on the same machine should be able to share this port.
Multiple lookup service on the same machine must, however, have different
unicast discovery/response ports. The "reggie" implementation of the
lookup service, which comes with the Sun' Jini release, includes an API
(the
DiscoveryAdmin
interface) that enables you to change
the default port number. Also, the -admin
GUI of the lookup
service browser enables you to configure specific ports. Such changes will
remain persistent. If you don't need consistent port numbers, you can just
fire off several reggies on the same host, and all reggies except the
first will automatically use a dynamically allocated port rather
than the standard one for unicast discovery.
Why must Service IDs be universally unique and why
must they be remembered after assigned?
Every Jini service is supposed to have a service ID that is globally unique. If a service has no service ID, it can receive one from a Jini lookup service by simply registering with a lookup service. Usually a service includes its service ID as part of the service item it sends to the lookup service when it is registered via the join process. Whenever a lookup service receives a service item that has no service ID, it assigns a new service ID to that service and sends the ID back to the service. The service is reponsible for remembering and using that same service ID forever after. In practice, Jini service providers (such as enabled devices) will likely come from the factory with service IDs already installed.
Services are required to maintain the same service ID throughout their lifetimes for several reasons. First, because a service always uses the same service ID, it can't be registered twice in the same lookup service. Every service registered in a lookup service has a unique service ID. If a service registers itself with a lookup service using a service ID that is being used by a service already registered in that lookup service, the already registered service will be replaced by the newly registering service. The lookup service interprets the newly registering service as a replacement (or new version) of the currently registered service with the same service ID.
Another reason services are required to always use the same service ID is that a client can determine that multiple services retrieved from multiple lookup services are actually the same service. If two services retrieved from two different lookup services have the same service ID, then those two services actually represent the same single service, which was registered in two different lookup services.
And lastly, a permanent service ID
allows lookup services to be queried by service ID. Queries based on
service ID allows enables clients to find exactly the service they
are looking for, independent of service and attribute object equality.
A service may be upgraded with a service object
that no longer implements the same interfaces as the previous version.
Similarly, a service may be upgraded with a new set of attributes that don't
have the same types or values as the previous versions. Such new
versions of a service may not match queries the matched the old version
of the same service.
Despite this potential for incompatible upgrades, clients (such as systems
administrators who want to track a service's status) can easily lookup
the service by Service ID. Because the service ID will never change, it
can be trusted to always match even if the service object and attributes
change drastically.
Can a client make persistent changes to the attributes of a service?
A client can't change the attributes of a service directly.
No methods that change attributes exist in the
ServiceRegistrar
interface. Such methods exist only
in the ServiceRegistration
interface, which is implemented
by the object returned when service is registered. Thus, a client
must go through the service to change to an attribute, and the service
has the option of denying the request.
Whether or not an attribute change is persistent across network failures
is also the responsibility of the service itself.
If a service wishes to be administratable, its service object should implement
the
net.jini.admin.Administratable
interface. This interface
defines just one method, getAdmin()
, which returns an
object that should implement the net.jini.admin.JoinAdmin
interface, as well as other service-specific admin interfaces.
If a service doesn't want anyone besides itself to change an attribute,
the Entry
class for that attribute should
implement the net.jini.lookup.entry.ServiceControlled
interface.
To what extent is Jini dependent upon IP?
The current implementation of Jini is IP based, though other protocols could potientially be supported in the future. Such support for other protocols may involve gateways to IP.
Currently, a device that wishes to participate in a Jini federation must somehow get an IP address. The device must in fact already have an IP address before it sends the presence announcement packet (multicast request protocol). The lookup service extracts the IP address from the presence announcement packet in order connect back to the device to complete the discovery process via the unicast discovery protocol.
The Jini specifications themselves are silent about the process by which devices will get IP addresses. In particular, the lookup service has no involvement in the process of assigning IP addresses. It is likely that IP addresses will be dynamically assigned by a DHCP server or some other automatic mechanism when a device goes on-line.
Other than the discovery protocols, the Jini specification is quite
independent of IP. Once a lookup service has been
found, everything is RMI, and Jini is shielded from protocol details.
Programmers who use the classes in the Jini API that support discovery,
such as
JoinManager
and LookupDiscovery
,
will find that the IP dependency is well hidden from their code.
In addition, the APIs that support leasing, distributed events, and
distributed transactions are independent of IP.
How can I control the lease length granted by
a lookup service when I register my service?
When you register a service either via the
ServiceRegistrar
or the JoinManager
, you can
specify a desired lease duration. The lookup service may grant a lease
whose duration is shorter (but not longer) than the requested duration.
In practice, a lookup service likely has a maximum lease length that it
observes when granting leases to registering services. You
can manage the maximum lease duration offered by a lookup service, if you have
permission, via the admin interface to the lookup service itself. For
the details, see the following text, which is based heavily on
a post written by Brian Murphy:
If you look at the code in RegistrarImpl.java
, you will see
private long minMaxServiceLease = 1000 * 60 * 5; private long minMaxEventLease = 1000 * 60 * 30;
These values indicate that, by default, the maximum length
of a service lease is 5 minutes; and the maximum length of
a lease on event notification is 30 minutes. When a service
requests a lease duration of Long.MAX_VALUE
(either
in the service itself or through the JoinManager
),
the lookup service interprets such a request to mean:
"assign a lease duration that is no greater than the default
maximum lease duration". Thus, if your service requests a lease
duration of Long.MAX_VALUE
,
then lookup will usually assign a duration that is 5 minutes
(or 30 minutes for event notifications); but might assign
a smaller duration. The Lookup Specification addresses this
issue.
If you want to assign longer lease durations, you can
change the default maximum values through the RegistrarAdmin
interface. This is something that the administrator of the
lookup would typically do based on the needs of the various
services that interact with the administrator's lookup.
To do this, you would get a reference to the RegistrarAdmin
through the Registrar
itself. For example,
ServiceRegistrar proxy = (CreateLookup.create(...)).reg; RegistrarAdmin adminProxy = (RegistrarAdmin)(((Administrable)proxy).getAdmin()); adminProxy.setMinMaxServiceLease(1000*60*25); adminProxy.setMinMaxEventLease(1000*60*90);
Now, when you ask for a lease duration of Long.MAX_VALUE
,
25 minutes or less will be granted for services; and 90
minutes or less will be granted for event notifications.
Note, that lookup services will not typically grant services not having the appropriate privledges the ability to change the lookup's configurable items. Usually the lookup service admin will have a GUI (possibily even a GUI registered as a service in the lookup itself) that is password protected; and which allows the administrator - and no one else - to change such items.
Because, in your test environment, you are both the creator/administrator of the lookup service and the creator of the service, you have the ability to change these items from your program.
One final note with respect to lease durations. At the
top of RegistrarImpl.java
, you will see the constant
MAX_LEASE (= 1000L * 60 * 60 * 24 * 365 * 1000)
This is the largest value that you can request the
lookup reset the default max lease duration to; anything
greater and you will get an
IllegalArgumentException
.
How do I perform advanced searches during lookup?
An example of this problem is a client that needs to find a display
service that is at least 640 by 480 pixels. Assume that the well-known
interface for displays is called Display
and that
Display
services usually register a Resolution
attribute that contains a screen width
and
height
in pixels. You could look for a
Display
service that has a resolution
of 640 by 480, but how
could you find a Display
that has at least 640 by 480
resolution?
Although the lookup service matching semantics only support exact matching,
you could first get an array of all the different resolutions supported by
currently registered
Display
services by using the
ServiceRegistrar.getFieldValues()
method.
Once you know the range of available resolutions, you could
set the attributeSetTemplates
field of the
ServiceTemplate
to perform exact match lookups for the resolutions
you are interested in.
What is the difference between Jini and CORBA?
CORBA is perhaps more akin to RMI than Jini, but CORBA includes a service called the trader service that is somewhat reminiscent of the Jini lookup service. CORBA, which stands for Common Object Request Broker Architecture, enables you to invoke methods on remote objects written in any programming language. RMI, which stands for Remote Method Invocation, enables you to invoke methods on Java objects in remote virtual machines. The objects needn't have been written in the Java programming language, but they do need to be running in a Java virtual machine. Thus, they most likely were written in some language that was compiled to Java class files, and by far the most common language compiled to Java class files is Java.
CORBA has a naming service that lets you look up a remote object by name and obtain a remote reference to it. RMI has a registry server that let you do the same thing: look up a remote object by name and obtain a remote reference to it. CORBA departs from RMI, however, in that to use the remote reference by invoking methods on a local stub, CORBA requires that the client have the definition of the stub locally. (In other words, CORBA requires that the code for the stub object be known to the developers that create the client.) RMI, by contrast, can send the class files for a stub object across the wire. Because an RMI client can dynamically load the code for a stub object, that code need not be known to the developers of the client.)
CORBA does offer a "dynamic invocation interface" that enables clients to use remote objects without the stub definition, but it is more complex to use than just invoking methods on a local stub. The difference in complexity is similar to the difference between using a Java object through its interface and using the same Java object via the reflection API.
Another difference between CORBA and RMI is that RMI lets you pass objects by value as well as by reference when you invoke a remote method. Up until the CORBA/IIOP 2.2 specification was released in 1998, CORBA only allowed you to pass objects by reference. The 2.2 specification added support for objects by value, but this functionality yet may not be supported in many CORBA implementations.
A significant difference between CORBA and RMI is that when a subclass
object is passed to a remote method by value, CORBA will truncate
the object to the type declared in the method parameter list. For
example, if a CORBA remote method expects an Animal
and the client
passes a Dog
(a subclass of Animal
), the remote object will receive
an Animal
(a truncated Dog
), not a Dog
. Because RMI can send the
class files for the Dog
across the network, the RMI remote object
will receive a Dog
. If the remote virtual machine has no idea what
a Dog
is, it will pull the class file for Dog
down across the
network.
As mentioned previously, CORBA does include one service that is reminescent of Jini's lookup service: the CORBA trader service. Instead of just supplying a name with which a remote object is associated, as you do with the CORBA naming service (or the RMI registry), you describe the type of remote object you are seeking. Similarly, you can look up a Jini service by type. The Jini lookup service offers a bit more flexibility in searching, however, because you can also search by a globally unique service ID and by attributes. But the most important difference between the CORBA trader service and the Jini lookup service lies in what they return as a result of the query. The CORBA trader service returns a remote reference to a matching remote object. The Jini lookup service returns a proxy object by value.
Thus, when you get a remote reference back from the CORBA trader
service (assuming you have the stub definition), you can talk to
the remote object by invoking methods on the local stub. The local
stub will talk across the network to the remote object via CORBA.
When you talk to a Jini service object, on the other hand, that
service object may not talk across the network at all. It may
implement the service in its entirety locally in the client's
virtual machine. Or, it may talk across the network to a server,
servers, or some hardware via sockets and streams. Or, the service
object may actually be an RMI stub that communicates across the
network to a remote RMI object via the RMI wire protocol. The
service object returned by Jini can use any network protocol to
communicate across the network. It could even use CORBA.
Should Jini service interfaces extend Remote?
When you design an object whose methods you want to invoke from a different virtual machine via RMI, you must design an interface through which the client interacts with your object. Your object -- called a remote object -- should extend some subclass of java.rmi.server.RemoteObject, such as UnicastRemoteObject or Activatable. Each interface through which clients will interact with your remote object should extend the tag interface java.rmi.Remote. The Remote interface identifies interfaces whose methods may be invoked from non-local virtual machines. Each method in an interface that extends Remote should declare java.rmi.RemoteException in its throws clause. The checked RemoteException indicates that some problem occurred in fulfilling the remote method invocation request.
When you design a Jini service interface, you define the way in which clients will talk to your Jini service. Clients will receive some implementation of your interface from the lookup service. This object will likely be passed as a parameter or return value of an RMI remote method invocation between the client and the lookup service.
In general, Jini service interfaces should not extend java.rmi.Remote, because that would imply that the service is implemented as an RMI remote object. It is more flexible to allow specific implementations of the service to bring about the Remote interface themselves if they are to be an RMI remote object. By not incorporating Remote in the service interface, you give people who provide implementations of the service the *option* of being an RMI remote object.
The methods of the service interface should, on the other hand, declare java.rmi.RemoteException in their throws clause. Any method that hopes ever to be invoked via RMI must declare java.rmi.RemoteException. Thus, to give providers of implementations of your service interface the option of being an RMI remote object, you must declare RemoteException in each method declared in the service interface.
Adding RemoteException to the throws clauses of the methods of your
Jini service interface indicates not only that RMI may be used by
implementations, but that the network in general may be used by
implementations. Thus, if an implementation of your Jini service
uses sockets and streams to talk across the network to a server, it
should indicate network problems by throwing some subclass of
RemoteException. Likewise, if an implementation uses CORBA or DCOM
to talk across the network to remote objects, it should throw some
subclass of RemoteException to indicate network problems.
Why does an application that exports an RMI remote object need
to be able to load the stub class?
A stub class is generated by rmic from the class file that defines a class for a remote object. The stub class implements all the same remote interfaces as the remote object's class. When a client holds a remote reference to a remote object, it is actually holding a local reference to a local instance of the stub class for that remote object. Because the stub class implements all the same remote interfaces as the remote object's class, the client can invoke any method on the stub through the remote interfaces that it could invoke on the remote object.
Since the client must instantiate a stub object, it makes sense that the client needs to be able to load the class file for the stub class. The reason the server must also be able to load the stub class is less obvious. A stub class can be distributed along with clients, which enables the client to load the stub class from a local repository, such as from its local class path. However, RMI offers an alternative that makes it unnecessary to distribute stub classes with clients.
In an RMI method invocation, objects can be passed as parameters, returned as return values, or thrown as exceptions, either by value or by reference. If an object is a remote object, it is passed by reference; otherwise, it is passed by value.
To pass an object by reference, the remote object itself is replaced at serialization time by its stub. In other words, to pass a remote object by reference, RMI passes its stub by value. Like any other serialized object RMI passes along the wire, it optionally annotates the serialized stream with a codebase URL. If a recipient of a serialized stub (an RMI client) doesn't have the class file for the stub class available locally, the recipient can go to the codebase URL to download the class file across the network. By enabling the mobility of stub classes across the network, RMI eliminates the necessity to distribute stub classes with clients.
To pass an object by value, the state of the object is serialized, and the serialized stream is optionally annotated with a codebase URL. The codebase specifies how the recipient of the serialized object can get hold of any class files necessary for deserialization that aren't available at the receiving end.
Now, back to the original question: Why does the server that
exports a remote object need to have the stub available? When a
server exports a remote object for use by a client, the server
needs both the remote object class itself and the stub class,
because in the process of exporting a remote object, an instance of
the stub is created in the local (server's) virtual machine. In the
process of exporting a remote object, the stub class is requested
of the class loader that loaded the remote object's class, so the
class must be available locally to that particular class loader.
Whenever a client requests a remote reference to the remote object
via the rmi registry naming server, the client is sent the
serialized stub object. Thus, even though the server application
typically doesn't use the stub class overtly, it's needed by RMI's
serialization machinery when the remote object is exported.
How should I partition classes at the RMI codebase among jar
and class files?
The two potentially contradictory goals you should aim for when thinking about how to partition the classes you need to make available through the RMI codebase are minimizing download time for needed classes and minimizing downloading of unneeded classes.
If you place only individual class files at the codebase, then clients won't have to download any class files they don't require. They will, instead, grab each class file they need individually. This approach eliminates the downloading of unneeded classes, however, it may not yield the minimum possible download time for needed classes.
When you place only individual class files at the codebase, clients must make a separate HTTP request for each class file. Were you to place all your class files in a single JAR file, only one HTTP request would be needed to grab the JAR file, which contains all the class files. This approach eliminates the time required by all those HTTP requests for individual class files, but forces all clients to download all class files that come in the JAR package, even if they only need one of those class files.
Nevertheless, depending on how big the JAR file is and how many classes contained in the JAR file are actually needed by a particular client, it is often more efficient to place the class files in a JAR. Not only does the client save time by only having to make one HTTP request, the contents of the JAR file can be compressed. (A JAR file, after all, is a ZIP file.)
Of course, if your JAR file contains many class files that may not be needed by many clients, it may still be more efficient to offer individual class files. An alternative, in-between approach that you can also consider taking when you've got many class files that many clients won't need is to distribute the class files among several JAR files. If you take this approach, you'll need to put a Class-Path: attribute in the manifest file of the initial JAR file that points to the other JAR files. You can place the classes that most clients will need in the initial JAR file, and classes that will be required less frequently in the other JAR files.
For more information, check out John McClain's presentation on "How to avoid Codebase Problems":
http://developer.jini.org:80/exchange/users/jmcclain/index.html
To help users view and edit the attributes attached to your Jini service, you can create entry beans for them. An entry bean is a class that follows the JavaBeans naming conventions, which serves as an adapter between a user and a service attribute entry. To view and manipulate the attribute entry, the user interacts with the entry bean.
To be a JavaBean, you need only declare a no-arg constructor and
implement Serializable
. Nevertheless, most beans go further than
that by declaring get and set methods for properties, addListener
and removeListener methods for events, and potentially providing
other support classes such as BeanInfo
s,
PropertyEditor
s, and Customizer
s.
An entry bean is any JavaBean that implements the EntryBean
interface:
package net.jini.lookup.entry.EntryBean; public interface EntryBean { public void makeLink(Entry e); public Entry followLink(); }
To connect an entry bean to its entry, you pass a reference to the
entry to makeLink()
. followLink()
returns a
reference to the Entry passed to makeLink()
.
You must give to each entry bean class a name consisting of the
name of the entry plus "Bean
." For example, an entry bean
for a "Provider
" attribute entry would have to be named:
"ProviderBean
".
To enable the bean to be found and loaded, you merely make the
class files for the bean (and any supporting class files, such
as class files for
Customizer
s, BeanInfo
s,
etc.) available at
the codebase that contains the class files for the attribute
entry. You needn't register the bean as an attribute. When
a tool goes looking for an entry bean to help a user interact
with an entry, the tool will look in the codebase specified
in the serialized image of the entry object.
How are lookups by type implemented?
Jini's lookup service enables clients to look for specific kinds of services. When multiple services of the desired kind exist, clients can use attributes to narrow their search and help them select the best service for their needs.
To specify the kind of service desired, clients specify Java types. The types specified are most often interfaces, but can also be classes. Because developers of Jini clients must indicate the kind of desired service with a Java type, the developer knows about the type at compile-time, and will therefore know how to user whatever object is returned by the lookup service.
To indicate a Java type when performing a lookup, clients must pass
to the service registrar's lookup()
method
an array of references to Class
instances that represent the desired
types in the client's virtual machine. Any registered service whose
service object is an instance of each type specified by the client
matches the lookup. When the lookup service looks through its set of
registered services, however, it merely compares the type names of
the types represented by the client's specified Class
instances with
the types names of the registered service objects. It doesn't compare
any class information.
Because the lookup service compares types by name only, two different types with the same fully qualified name would match. Nevertheless, when the service object for such a mismatched type gets back to the client, however, the deserialization process would detect the mismatch and throw an exception.
The reason the lookup service can get by with performing merely a string compare on the type names is that:
com.ibm
namespace have
the same fully qualified name, and they aren't supposed to let the
world ever see anything they made whose name doesn't start with
"com.ibm"
.
Yes. The ServiceRegistrar interface includes a notify()
method:
public EventRegistration notify(ServiceTemplate tmpl, int transitions, RemoteEventListener listener, MarshalledObject handback, long leaseDuration) throws RemoteException;
You invoke notify()
to register yourself (or some other listener)
as interested in receiving a distributed event whenever the services
that match the passed ServiceTemplate
undergo a state change
described by the transitions parameter.
The transitions parameter is a bitwise OR of any non-empty set of
these three values, which are defined as constants in
ServiceRegistrar
:
TRANSITION_MATCH_MATCH TRANSITION_MATCH_NOMATCH TRANSITION_NOMATCH_MATCH
You build the ServiceTemplate
for notify()
in the same way you build
it for lookup()
. You can indicate explicit types, a service ID,
attributes, which must exactly match, or wildcards in any of those
fields, which match anything. The transitions are based on a change
(or non-change) in the status of what matches your ServiceTemplate
before and after any operation performed by anyone on the lookup
service.
For example, TRANSITION_MATCH_MATCH
indicates that at least one
service item matched your template before and after an operation.
TRANSITION_MATCH_NOMATCH
indicates that, although at least one
particular service item matched your template before an operation,
it no longer matched your template after the operation.
To receive notification when new services are added to a lookup
service, therefore, you simply specify a template that matches any
service, and pass
TRANSITION_NOMATCH_MATCH
as the transition
to the notify()
method.
Does the discovery protocol limit Jini within a single subnet?
The discovery protocol is really three protocols in one:
When a service provider or client finds itself connected to a new and unfamiliar network, it sends out a presence announcement on a well-known multicast port. This is the multicast request protocol. Lookup services monitor this well-known port. When a lookup service receives a presence announcement, it inspects the packet and decides whether or not to contact the sender. If it decides to make contact, it establishes a direct unicast connection to a host and port included in the presence announcement. The service provider or client sends a ping across this direct connection, and the lookup service responds with a service registrar object. The ping/service registrar conversation is the unicast discovery protocol.
Lookup services may also periodically send a presence announcement to a well-known multicast port, which clients and service providers can monitor. This is the multicast announcement protocol. If a client or service provider receives such a presence announcement, they can make a direct unicast connection back to a host and port number included in the presence announcement. The two parties perform the unicast discovery protocol across this connection: the client or service provider sends a ping, and the lookup service sends a service registrar object.
The unicast discovery protocol can also be used when a client or service provider has a long-term relationship with a lookup service. A client or service provider can simply make a direct unicast connection to a lookup service, and exchange a ping for a service registrar object.
None of these three discovery sub-protocols is necessarily limited to a single
subnet. The unicast discovery protocol, because it involves a known host and
port where the lookup service resides, could be used across the internet. The
other two protocol, because they involve multicast, will be more
geographically limited. Nevertheless, whether or not a multicast packet
reaches beyond the borders of the subnet in which it is launched depends
on how the local system administrators set up the nearby gateways. A
multicast packet may be limited to a single subnet, or it may migrate to
multiple nearby subnets.
How are service IDs generated such that they can be globally
unique?
The first time a Jini service registers itself with a lookup service, the lookup service creates a service ID for that service. The service provider is supposed to remember this ID forever. Every time it registers its service with any lookup service from that point forward, it should specify its service ID.
The service ID provided by the lookup service is supposed to be globally unique. In other words, a particular service ID, if it is ever generated, should be generated only once by any lookup service, anywhere in the universe, at any time. How does this work?
First of all, the service ID is 128 bits long. The size of the ID itself makes it unlikely that any two randomly generated service IDs would come out to be the same value, so long as the random number generators started with different seed values.
The Jini lookup service uses a technique that includes randomization, but
also includes other techniques that in effect guarantees the uniqueness of
each service ID until the year 3400. In 60 of the
128 bits, the lookup service expresses the current system time in the number
of 100 nanosecond ticks since 1582. The rest of the ID is a random number
and, in some lookup service implementations, a unique host address for the
lookup service.
What are the ServiceRegistrar's browsing methods for, and how are
they used?
The ServiceRegistrar
has three methods that are called "browsing methods,"
getServiceTypes()
, getEntryClasses()
, and getFieldValues()
. These three methods
are called "browsing methods" because their intended purpose is to enable clients
to browse the services and attributes in the lookup service.
The getServiceTypes()
method takes a ServiceTemplate
(the same ServiceTemplate
that's passed
to the lookup()
methods) and a String
prefix. The method returns an array of Class
instances representing the most specific types (classes or interfaces) of the service
objects that match the template which are neither equal to, nor a superclass of, any
of the types specified in the template and that have names that start with the specified
prefix. The service object or objects for whom Class
instances are returned are all
instances of all the types (if any) passed in the template, but the Class
instances
returned are all more specific than (are subclasses or subinterfaces of) the types specified
in the template. Each class appears only once in the returned array, and the order of
the classes in the returned array is arbitrary.
The getEntryTypes()
method takes a ServiceTemplate
, and returns an array of Class
instances
that represent the most specific classes of entries for those service items that match
the template which either don't match any entry template or are a subclass of an
entry template. Each class appears only once in the returned array, and the order of
the classes in the returned array is arbitrary.
The getFieldValues()
method takes a ServiceTemplate
, an integer index, and a String
field
name. The method returns an array of Object
s for the named field of all instances of the
entry that appears in the ServiceTemplate
's Entry[]
array at the passed index in any
matching service item. Each object of a particular class and value appears only once in
the returned array, and the order of the Object
values in the returned array is arbitrary.
The behavior and purpose of these methods can be obscure. A good way to think of them is
enabling clients to sequentially narrow queries of the lookup service. A client, such
as a graphical lookup service browser, could begin by invoking getServiceTypes()
with
an empty template. The getServiceTypes()
method returns all possible service types
registered in the lookup service, which the browser could display. The user could
select one or more type, then push the Requery button. The browser would add that type to
the service template, and invoke getServiceTypes()
again. A smaller list of types would be
returned, and the browser would display those. The user could select one and press
a Entries button. The browser would form a template with the most recently selected
service type or types, and invoke getEntryTypes()
. The getEntryTypes()
method would
return an array of entry classes, which the browser could then display. The user could
select some entries, and a field of a selected entry, and push a Fields button. The
browser would build a template using the currently selected service and entry types, and
pass the index of the entry class in which the user selected the field, and the name
of the selected field to getFieldValues()
. The browser would display all the values that
getFieldValues()
returned. The user could use these values to further narrow the search
for a service. Eventually, the user could select and use a service.
Thus, these three "browsing methods" are geared towards helping clients, whether a human
user is involved or not, to browse the lookup service. The arrays returned from the
browsing methods can help the client further refine its queries, ultimately resulting
in a
ServiceTemplate
that, when passed to lookup()
, returns the most appropriate
service object.
Why do I get com.sun.rmi.rmid.ExecOptionPermission
exceptions when
starting Jini?
A ExecOptionPermission
exception, such as the one
shown below, most likely means that you're having trouble with
the new Java/RMID security system introduced in JDK1.3 and backported to
JDK1.2.2.
java.rmi.activation.ActivateFailedException: failed to activate object; nested exception is: java.security.AccessControlException: access denied (com.sun.rmi.rmid.ExecOptionPermission -Djava.security.policy=)
An explanation of the new security policy can be found here:
http://developer.java.sun.com/developer/products/jini/execpolicy.html
Starting RMID with the sun.rmi.activation.execPolicy
property set
to none
will turn off security checks, and yield a "policy.all" like behavior:
rmid -J-Dsun.rmi.activation.execPolicy=none
Iain Shigeoka created a new ultra-promiscuous policy file, that will also turn off security checks in RMID. You can download this file as part of the jini-tools at: