A client locates a service by querying a lookup service. In order to do this, it must first locate such a service. On the other hand, a service must register itself with the lookup srvice, and in order to do so it must also first locate a service.
The initial phase of both a client and a service is thus discovering a lookup service. Such a service (or set of services) will usually have been started by some independent mechanism. The search for a lookup service can be done either by unicast or by broadcast.
Unicast discovery can be used when you already know the machine
on which the lookup service resides, so you can ask for it
directly. This is expected to be used for a service that
is outside of your local network, which you know the
address of anyway (possibly advertised in some newsgroup,
or maybe even on TV!).
The class LookupLocator
in package
net.jini.core.discovery
is used for this.
There are two constructors,
package net.jini.core.discovery;
public Class LookupLocator {
LookupLocator(java.lang.String url)
throws java.net.MalformedURLException;
LookupLocator(java.lang.String host,int port);
}
For the first constructor, the URL must be of the form
jini://host/
or jini://host:port/
.
If no port is given, it defaults to 4160.
No unicast discovery is performed at this stage, though.
The following program creates some objects with valid and invalid host/URLs. They are only checked for syntactic validity rather than existence as URLs:
import net.jini.core.discovery.LookupLocator;
/**
* InvalidLookupLocator.java
*
*
* Created: Tue Mar 9 14:14:06 1999
*
* @author Jan Newmarch
* @version
*/
public class InvalidLookupLocator {
static public void main(String argv[]) {
new InvalidLookupLocator();
}
public InvalidLookupLocator() {
LookupLocator lookup;
// this is valid
try {
lookup = new LookupLocator("jini://localhost");
System.out.println("First lookup creation succeeded");
} catch(java.net.MalformedURLException e) {
System.err.println("First lookup failed: " + e.toString());
}
// this is probably an invalid URL,
// but the URL is syntactically okay
try {
lookup = new LookupLocator("jini://ABCDEFG.org");
System.out.println("Second lookup creation succeeded");
} catch(java.net.MalformedURLException e) {
System.err.println("Second lookup failed: " + e.toString());
}
// this IS a malformed URL
try {
lookup = new LookupLocator("A:B:C://ABCDEFG.org");
System.out.println("Third lookup creation succeeded");
} catch(java.net.MalformedURLException e) {
System.err.println("Third lookup failed: " + e.toString());
}
// this is valid
lookup = new LookupLocator("localhost", 80);
System.out.println("Fourth lookup creation succeeded");
}
} // InvalidLookupLocator
This program might not run as is, due to security issues. If that is the case, see the chapter on Security.
Search and lookup is performed by the method getRegistrar()
of the LookupLocator
which returns an object of class
ServiceRegistrar
.
public ServiceRegistrar getRegistrar()
throws java.io.IOException,
java.lang.ClassNotFoundException
The ServiceRegistrar
is discussed in detail later. By this stage, the program looks like
import net.jini.core.discovery.LookupLocator;
import net.jini.core.lookup.ServiceRegistrar;
/**
* UnicastRegistrar.java
*
*
* Created: Fri Mar 12 22:34:53 1999
*
* @author Jan Newmarch
* @version
*/
public class UnicastRegistrar {
static public void main(String argv[]) {
new UnicastRegistrar();
}
public UnicastRegistrar() {
LookupLocator lookup = null;
ServiceRegistrar registrar = null;
try {
lookup = new LookupLocator("jini://localhost");
} catch(java.net.MalformedURLException e) {
System.err.println("Lookup failed: " + e.toString());
System.exit(1);
}
try {
registrar = lookup.getRegistrar();
} catch (java.io.IOException e) {
System.err.println("Registrar search failed: " + e.toString());
System.exit(1);
} catch (java.lang.ClassNotFoundException e) {
System.err.println("Registrar search failed: " + e.toString());
System.exit(1);
}
// the code takes separate routes from here for client or service
}
} // UnicastRegistrar
If the location of a lookup service is unknown, it is neccessary
to make a broadcast search for one. The class
LookupDiscovery
in package
net.jini.discovery
is used for this.
There is a single constructor
LookupDiscovery(java.lang.String[] groups)
The parameter to the LookupDiscovery
constructor
can take three cases
null
, or LookupDiscovery.ALL_GROUPS
,
means to attempt to discover all reachable lookup services.
LookupDiscovery.NO_GROUPS
,
means that the object is created, but no search is performed. In this
case, the method setGroups()
will need to be called
in order to perform a search.
A broadcast is an open-ended call across the (local??? What is the
boundary of search???) network, expecting lookup
services to reply as they receive it. Doing so may take time, and there
will generally be an unknown number of lookup services that can reply.
To handle this indeterminacy, the LookupDiscovery
object
can have a listener registered with it that is invoked as each reply comes in.
public void addDiscoveryListener(DiscoveryListener l)
The listener must implement the DiscoveryListener
interface:
package net.jini.discovery;
public abstract interface DiscoveryListener {
public void discovered(DiscoveryEvent e);
public void discarded(DiscoveryEvent e);
}
The discovered()
method is invoked whenever a lookup service
has been discovered. The API recommends that this method should return
quickly, and not make any remote calls. However, for a service it is
the natural place to register the service, and for a client it is
the natural place to ask if there is a service available and to invoke
this service. It may be better to perform these lengthy operations
in a separate thread.
There are other timing issues involved: when the
DiscoveryListener
is created, the broadcast is made.
After this, a listener is added to this discovery object. What happens
if replies come in very quickly, before the listener is added?
The specification guarantees that these replies will be buffered and
not lost (is there a limit to the buffer size???).
Conversely, no replies may come in for a long time - what is the
application supposed to do in the meantime? It cannot simply exit,
because then there would be no object to reply to! it has to be made
persistent enough to last till replies come in. One way of handling
this is if the application has a GUI interface - then it will stay
till the user dismisses it. Another is to go to sleep for, say,
a thousand seconds.
The discarded()
method is invoked
whenever a lookup service is discarded (meaning ???).
discover()
method is a
DiscoveryEvent
object.
package net.jini.discovery;
public Class DiscoveryEvent {
public net.jini.core.lookup.ServiceRegistrar[] getRegistrars();
}
This has one public method, getRegistrars()
which returns an array of ServiceRegistrar
objects.
(How can more than one be returned???). Each one of these is the same
class as the object returned from a unicast search for a lookup service.
By this stage the program looks like
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.core.lookup.ServiceRegistrar;
/**
* MulticastRegistrar.java
*
*
* Created: Fri Mar 12 22:49:33 1999
*
* @author Jan Newmarch
* @version
*/
public class MulticastRegistrar implements DiscoveryListener {
static public void main(String argv[]) {
new MulticastRegistrar();
}
public MulticastRegistrar() {
LookupDiscovery discover = null;
try {
discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
} catch(Exception e) {
System.err.println(e.toString());
System.exit(1);
}
discover.addDiscoveryListener(this);
// stay around long enough to receive replies
try {
Thread.currentThread().sleep(1000000L);
} catch(java.lang.InterruptedException e) {
// do nothing
}
}
public void discovered(DiscoveryEvent evt) {
ServiceRegistrar[] registrars = evt.getRegistrars();
for (int n = 0; n < registrars.length; n++) {
ServiceRegistrar registrar = registrars[n];
// the code takes separate routes from here for client or service
}
}
public void discarded(DiscoveryEvent evt) {
}
} // MulticastRegistrar
The ServiceRegistrar
is an abstract class which is
implemented by each lookup service. The actual details of this
implementation are not relevant here. The role
of a ServiceRegistrar
is to act as a proxy
for the lookup service. This proxy runs in the application,
which may be a service or a client.
This is the first object that is moved from one Java process to another in Jini. It is shipped from the lookup service to the application looking for the lookup service, using RMI. From then it runs as an object in the application's address space, and the application makes normal method calls to it. When needed, it communicates back to its lookup service, but does so without explicit RMI calls being needed by the application. Presumably it caches some info about the lookup service - exactly what??? The set of services???
This object has two major methods, one used by a service attempting to register
public ServiceRegistration register(ServiceItem item,
long leaseDuration)
throws java.rmi.RemoteException
and the other(s) by a client trying to locate a particular service
public java.lang.Object lookup(ServiceTemplate tmpl)
throws java.rmi.RemoteException;
public ServiceMatches lookup(ServiceTemplate tmpl,
int maxMatches)
throws java.rmi.RemoteException;
The details of these are given later. For now, an overview will suffice.
A service will register an object (that is, an instance of a class), and a set of attributes for that object. For example, a printer may specify that it can handle Postscript documents, or a toaster that it can deal with frozen slices of bread. The service may register itself, or it may register a proxy that can act on its behalf. Note carefully: the registered object will be shipped around using RMI. When it finally gets to run, it may be a long way away from where it was originally created.
A client is trying to find a service, using some properties of the service that it knows about. Whereas the service can export a live object, the client cannot use a service object as a property - because then it would already have the thing, and wouldn't need to try to find one! What it can do is to use a class object, and try to find if there are any instances of this class lying around the network. In addition, it may specify a set of attribute values that it requires from the service.
The next step is to look at the possible forms of attribute values,
and how matching will be performed. This is done using Jini
Entry objects.
The simplest services, and the least demanding clients, will not
require any attributes: the Entry[]
array will be
null
. You may wish to skip ahead to
service registration or to
client search