91.

previous chapter table of contents next chapter
  

Broadcast Discovery

If the location of a lookup service is unknown, it is necessary to make a broadcast search for one. UDP supports a multicast mechanism that the current implementations of Jini use. Because multicast is expensive in terms of network requirements, most routers block multicast packets. This usually restricts broadcasts to a local area network, although this depends on the network configuration and the time-to-live (TTL) of the multicast packets.

There can be any number of lookup services running on the network accessible to the broadcast search. On a small network, such as a home network, there may be just a single lookup service, but in a large network there may be many ”perhaps one or two per department. Each one of these may choose to reply to a broadcast request.

Groups

Some services may be meant for anyone to use, but some may be more restricted in applicability. For example, the Engineering department may wish to keep lists of services specific to that department. This may include a departmental diary service, a departmental inventory, etc. The services themselves may be running anywhere in the organization, but the department would like to be able to store information about them and to locate them from their own lookup service. Of course, this lookup service may be running anywhere , too!

So there could be lookup services specifically for a particular group of services, such as the Engineering department services, and others for the Publicity department services. Some lookup services may cater to more than one group ”for example, a company may have a lookup service to hold information about all services running for all groups on the network.

When a lookup service is started, it can be given a list of groups to act for as a command line parameter. A service may include such group information by giving a list of groups that it belongs to. This is an array of strings, like this:

 String [] groups = {"Engineering dept"}; 

LookupDiscovery

The LookupDiscovery class in package net.jini.discovery is used for broadcast discovery. There is a single constructor:

 LookupDiscovery(java.lang.String[] groups) 

The parameter in the LookupDiscovery constructor can take three possible values:

  • null , or LookupDiscovery.ALL_GROUPS , means that the object should attempt to discover all reachable lookup services, no matter which group they belong to. This will be the normal case.
  • An empty list of strings, or 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 non-empty array of strings can be given. This will attempt to discover all lookup services in that set of groups.

DiscoveryListener

A broadcast is a multicast call across the network, and lookup services are expected to reply as they receive the call. Doing so may take time, and there will generally be an unknown number of lookup services that can reply. To be notified of lookup services as they are discovered , the application must register a listener with the LookupDiscovery object, as follows :

 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, the discovered() method is the natural place for a service to register, and it is also the natural place for a client to ask if there is a service available and to invoke the 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, and after this, a listener is added to this discovery object. What happens if replies come in very quickly, before the listener is added? The "Jini Discovery Utilities Specification" guarantees that these replies will be buffered and delivered when a listener is added. 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 until replies come in. One way of handling this is for the application to have a GUI interface, in which case the application will stay until the user dismisses it. Another possibility is that the application may be prepared to wait for a while before giving up. In that case, the main() method could sleep for, say, ten seconds and then exit. This will depend on what the application should do if no lookup service is discovered.

The discarded() method is invoked whenever the application discards a lookup service by calling discard() on the registrar object.

DiscoveryEvent

The parameter of the discovered() method of the DiscoveryListener interface 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. Each one of these implements the ServiceRegistrar interface, just like the object returned from a unicast search for a lookup service. More than one ServiceRegistrar object can be returned if a set of replies have come in before the listener was registered ”they are collected in an array and returned in a single call to the listener. A UML sequence diagram augmented with jagged arrows showing the network broadcast and replies is shown in Figure 3-2.

click to expand
Figure 3-2: UML sequence diagram for discovery

In Figure 3-2, the creation of a LookupDiscovery object starts the broadcast search, and it returns the discover object. The MulticastRegister adds itself as a listener to the discover object. The search continues in a separate thread, and when a new lookup service replies, the discover object invokes the discovered() method in the MulticastRegister , passing it a newly created DiscoveryEvent . The MulticastRegister object can then make calls on the DiscoveryEvent , such as getRegistrars() , which will return suitable ServiceRegistrar objects. There is no line connecting to the ServiceRegistrar because the DiscoveryEvent creates the ServiceRegistrar somehow, but the actual mechanism that is used is hidden in the implementation of the DiscoveryEvent .

A MulticastRegister program that implements multicast searches for lookup services would look like this:

 package basic; import net.jini.discovery.LookupDiscovery; import net.jini.discovery.DiscoveryListener; import net.jini.discovery.DiscoveryEvent; import net.jini.core.lookup.ServiceRegistrar; /**  * MulticastRegister.java  */ public class MulticastRegister implements DiscoveryListener {     static public void main(String argv[]) {         new MulticastRegister();     // stay around long enough to receive replies     try {             Thread.currentThread().sleep(10000L);     } catch(java.lang.InterruptedException e) {         // do nothing     }     }     public MulticastRegister() {     System.setSecurityManager(new java.rmi.RMISecurityManager());         LookupDiscovery discover = null;         try {             discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);         } catch(Exception e) {             System.err.println(e.toString());             e.printStackTrace();             System.exit(1);         }         discover.addDiscoveryListener(this);     }     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         System.out.println("found a service locator");       }     }       public void discarded(DiscoveryEvent evt) {     } } // MulticastRegister 

Staying Alive

In the preceding constructor for the MulticastRegister program, we create a LookupDiscovery object, add a DiscoveryListener , and then the constructor terminates. The main() method, having called this constructor, promptly goes to sleep. What is going on here? The constructor for LookupDiscovery actually starts up a number of threads to broadcast the service and to listen for replies "(see chapter 21).

When replies come in, the listener thread will call the discovered() method of the MulticastRegister . However, these threads are daemon threads. Java has two types of threads ”daemon and user threads ”and at least one user thread must be running or the application will terminate. All these other threads are not enough to keep the application alive, so it keeps a user thread running in order to continue to exist.

The sleep() method ensures that a user thread continues to run, even though it apparently does nothing. This will keep the application alive, so that the daemon threads (running in the "background") can discover some lookup locators. Ten seconds (10,000 milliseconds ) is long enough for that. To stay alive after this ten seconds expires requires either increasing the sleep time or creating another user thread in the discovered() method. In Chapter 7, use is made of a useful constant, Lease.FOREVER . It is tempting to use the FOREVER constant if you want a thread to sleep forever. While the "leasing" system understands this FOREVER constant, the standard Java sleep() method does not treat it any special way and merely uses its Long.MAX_VALUE value and treats it as the maximum value of a long, so that it just sleeps for a very lengthy period.

I have placed the sleep() call in the main() method. It is perfectly reasonable to place it in the application constructor, and some examples do this. However, it looks a bit strange in the constructor, because it looks like the constructor does not terminate (so is the object created or not?), so I prefer this placement. Note that although the constructor for MulticastRegister will have terminated without us assigning its object reference, a live reference has been passed into the discover object as a DiscoveryListener , and it will keep the reference alive in its own daemon threads. This means that the application object will still exist for its discovered() method to be called.

Any other method that results in a user thread continuing to exist will do just as well. For example, a client that has an AWT or Swing user interface will stay alive because there are many user threads created by any of these GUI objects.

For services, which typically will not have a GUI interface running, another simple way to keep them alive is to create an object and then wait for another thread to notify() it. Since nothing will, the thread (and hence the application) stays alive. Essentially, this is an unsatisfied wait that will never terminate ”usually an erroneous thing to do, but here it is deliberate :

 Object keepAlive = new Object(); synchronized(keepAlive) {     try {         keepAlive.wait();     } catch(InterruptedException e) {         // do nothing     } } 

This will keep the service alive indefinitely, and it will not terminate unless interrupted . This is unlike sleep() , which will terminate eventually.

Running the MulticastRegister

The MulticastRegister program needs to be compiled and run with jini-core.jar and jini-ext.jar in its CLASSPATH . The extra jar file is needed because it contains the class files from the net.jini.discovery package. When run, the program will attempt to find all the service locators that it can. If there are none, it will find none ”pretty boring. So one or more service locators should be set running in the network or on the local machine. Service locators running in the network must be accessible by multicast calls or they will not be found. This usually means that they will have to be on the same LAN as the MulticastRegister program.

This program will receive ServiceRegistrars from the service locators. However, it does so with a simple readObject() on a socket connected to a service locator, and so does not need any additional RMI support services, such as rmiregistry .

Broadcast Range

Services and clients search for lookup locators using the multicast protocol by sending out packets as UDP datagrams. It makes announcements on UDP 224.0.1.84 on port 4160. How far do these announcements reach? This is controlled by two things:

  • the time-to-live (TTL) field on the packets
  • the network administrator settings on routers and gateways

By default, the current implementation of LookupDiscovery sets the TTL to 15. Common network administrative settings restrict such packets to the local network. However, the TTL can be changed by giving the system property net.jini.discovery.ttl a different value. However, be careful about setting this; many people will get irate if you flood the networks with multicast packets.

  


A Programmer[ap]s Guide to Jini Technology
A Programmer[ap]s Guide to Jini Technology
ISBN: 1893115801
EAN: N/A
Year: 2000
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net