Java Dynamic Management Kit 5.0 Tutorial

Chapter 17 Discovery Service

The discovery service enables you to discover Java dynamic management agents in a network. This service relies on a discovery client object that sends out multicast requests to find agents. In order to be discovered, an agent must have a registered discovery responder in its MBean server. Applications can also use a discovery monitor that detects when discovery responders are started or stopped.

The combination of these functions enables interested applications to establish a list of active agents and keep it up to date. In addition to knowing about the existence of an agent, the discovery service provides the version information from an MBean server's delegate and the list of communication MBeans that are currently registered. The discovery service uses the term communicators to designate a set of MBeans consisting of protocol adaptors and connector server objects.

Often, the discovery client and the discovery monitor are located in a manager application that wants to know about the available agents. However, agent applications are free to use the discovery service because they might require such information for cascading (see “Cascading Agents”) or for any other reason. To simplify using the discovery service in an agent, all of its components are implemented as MBeans.

The code samples in this topic are from the file in the Discovery directory located in the main examplesDir (see “Directories and Classpath” in the Preface).

This chapter covers the following topics:

Active Discovery

In active discovery, the discovery client initiates searches for agents on the network. It involves a discovery client that sends the discovery request and a discovery responder in each agent that responds. Each instance of the responder supplies the return information about the agent in which it is registered. The return information is represented in the discovery client by a vector of discovery response objects.

The application containing the discovery client can initiate a search at any time. For example, it might do a search when it is first started and periodically search again for information about the communicators that might have changed. For each search, the discovery client broadcasts a request and waits for the return information from any responders.

In the following sections, we describe each of these objects in further detail.

Discovery Client

The DiscoveryClient class provides methods to discover agents. The active discovery operation sends a discovery request to a multicast group and waits for responses. These messages are proprietary and are not exposed to the user. Discovery clients can only discover agents listening on the same multicast group and port, so your design must coordinate this information between the discovery client and responders.

You can instantiate and perform searches from multiple discovery clients in a single application. Each discovery client can be configured to use different multicast groups or ports, enabling you to discover different groups of agents.

Since Java DMK 5.0, the discovery services enable users to specify a local interface from which to send out multicast messages. This is useful when working on a system that has multihome interfaces connecting to disconnected multinetworks. In addition, the DiscoveryResponder constructor enables you to specify the host address to be used to build the discovery response. The address can be specified either as an IPV4 or IPv6 address, or as a host name.

In our example, the discovery client is in an agent application: we register it as an MBean and interact with it through the MBean server.


Example 17–1 Instantiating and Initializing a Discovery Client

// build the DiscoveryClient MBean ObjectName
//
ObjectName discoveryClientMBeanObjectName =
    new ObjectName(domain + "name=myDiscoveryClient") ;

// Create, register and start the DiscoveryClient MBean
//
try {
    ObjectInstance discoveryClientObjectInstance =
        myMBeanServer.createMBean(
            "com.sun.jdmk.discovery.DiscoveryClient",
            discoveryClientMBeanObjectName) ;
    myMBeanServer.invoke (discoveryClientMBeanObjectName,
        "start", null, null) ;

} catch(Exception e) {
    e.printStackTrace();
    System.exit(1);
}

The default multicast group is 224.224.224.224 and the default port is 9000. These can be set to other values through the multicastGroup and multicastPort attributes, but only when the state of the discovery client is OFFLINE. Before initiating searches, you must call the discovery client's start method. This will create its multicast socket and join the multicast group used for broadcasting its discovery request.

The scope of the discovery request depends on the time-to-live used by the multicast socket. Time-to-live is defined by the Java class java.net.MulticastSocket to be a number between 1 and 255. By default, the time-to-live is 1, which corresponds to the host's local area network. You can modify this value at any time by setting the discovery client's TimeToLive attribute.

By default, a discovery client waits for responses for one second after it has sent a discovery request. This period can be customized by setting a value in milliseconds for its TimeOut attribute. When setting this attribute, you should take into account estimated time for a round-trip of a network packet using the given time-to-live.

Performing a Discovery Operation

An application triggers a search operation by invoking the findMBeanServers or findCommunicators methods on an active DiscoveryClient object. Using the current settings, it will send the multicast request and block for the timeout period. At the end of the timeout period, these methods return the responses that were received.

Both methods return a vector of DiscoveryResponse objects. This class exposes methods for retrieving information about the MBean server and the registered communicator MBeans in the agent. The MBean server information is the same as that exposed by that agent's MBean server delegate. The communicators are identified by ConnectorAddress objects and indexed by object name in a hash table.

Both search methods return the information about the agent's MBean server. The hash table of communicator MBeans is always empty for discovery responses returned by the findMBeanServers method. Otherwise, you can extract object names and protocol information from the hash table. One way of distinguishing the communicator MBeans is to rely on the default names provided by the ServiceName class.


Note –

All discovery messages sent between components of the discovery service are compatible between applications running different versions of the Java SDK or different versions of the Java Dynamic Management Kit (4.x only). However, these different configurations are not compatible for subsequent management operations through connectors. You can use the getImplementationVersion method of the DiscoveryResponse object to determine both the Java SDK and product version numbers.


In our example, we request all information about the agents and use a simple subroutine to print out all information in the discovery responses.


Example 17–2 Performing a Discovery Operation

// Discover all JDMK agents with a registered discovery responder
//
Vector discoveryResponses = (Vector) myMBeanServer.invoke (
    discoveryClientMBeanObjectName,"findCommunicators", null, null) ;

echo("We have found " + discoveryResponses.size() + " JDMK agent(s): ");
for (Enumeration e = discoveryResponses.elements();
     e.hasMoreElements();) {
    DiscoveryResponse discoveryResponse =
        (DiscoveryResponse)e.nextElement() ;
    printDiscoveryResponse(discoveryResponse) ;
}

[...]

private void printDiscoveryResponse(DiscoveryResponse discoveryResponse) {

    // display information about the agent's MBean server
    //
    echo("\t MBeanServerId = " + discoveryResponse.getMBeanServerId())  ;
    echo("\t\t host name        = " + discoveryResponse.getHost())  ;
    [...]

    // display information about communicator objects, if any
    //
    if (discoveryResponse.getObjectList() != null) {
    	for( Enumeration e= discoveryResponse.getObjectList().keys();
             e.hasMoreElements(); ) {
            ObjectName o = (ObjectName) e.nextElement();
            echo("\t\t Communicator name        = " + o ) ;
    	}
    }
}

On the agent side, the discovery responder automatically replies to discovery requests. Any active, registered responder in the same multicast group that is reached within the given time-to-live of the request will respond. It will automatically gather the requested information about its MBean server and send the response. The settings of the responder do not affect its automatic reply to discovery requests. In Discovery Responder we will cover how its settings control passive discovery.

In active discovery, the discovery client controls all parameters of a search it initiates, including the response mode of the discovery responder. The discovery client determines whether responses are sent back on a different socket (unicast) or sent to the same multicast group. The default is unicast: if you want to use the multicast response mode, set the PointToPointResponse attribute to false before initiating the discovery.

Unicast Response Mode

When the PointToPointResponse boolean attribute is true, the discovery client specifies unicast mode in its discovery requests. The responder will create a datagram socket for sending the response only to the discovery client. As shown in the following diagram, each responder will send its response directly back to the discovery client. The datagram socket used by each responder is bound to its local host address; this cannot be customized.

Figure 17–1 Unicast Response Mode

Unicast response mode

Multicast Response Mode

When the PointToPointResponse boolean attribute is false, the discovery client specifies multicast mode in its requests. The discovery responder will use the existing multicast socket to send response, broadcasting it to the same multicast group as the request. As shown in the following diagram, every member of the multicast group will receive the message, but only the discovery client can make use of its contents. Multicast mode avoids having to open another socket for the response, but all of the responses will create traffic in each application's socket.

Figure 17–2 Multicast Response Mode

Multicast response mode

Passive Discovery

In passive discovery, the entity seeking knowledge about agents listens for the activation or deactivation of their discovery responders. When discovery responders are started or stopped, they send out a proprietary message that contains all discovery response information. The DiscoveryMonitor object waits to receive any of these messages from the multicast group.

A discovery monitor is often associated with a discovery client. By relying on the information from both, you can keep an up-to-date list of all agents in a given multicast group.

Figure 17–3 Passive Discovery of Discovery Responders

Diagram showing passive discovery of discovery responders

Therefore, configuring and starting the discovery responder is an important step to the overall discovery strategy of your applications.

Discovery Responder

The agents that are configured to be discovered must have an active DiscoveryResponder registered in their MBean server. The responder plays a role in both active and passive discovery:

Both types of messages are proprietary and their contents are not exposed to the user. These messages contain information about the MBean server, its delegate's information and a list of communicator MBeans, unless not requested by the discovery client.

In our example we create the discovery responder in the MBean server and then activate it.


Example 17–3 Initializing a Discovery Responder

// Set the domain name for the demo
//
String domain = "DiscoveryDemo:" ;

// build the DiscoveryResponder MBean ObjectName
//
ObjectName discoveryResponderMBeanObjectName =
    new ObjectName(domain + "name=myDiscoveryResponder");

// Create and register the DiscoveryResponder MBean
//
try {
    ObjectInstance discoveryResponderObjectInstance =
        myMBeanServer.createMBean(
            "com.sun.jdmk.discovery.DiscoveryResponder",
            discoveryResponderMBeanObjectName) ;
    // we don't start the responder until our monitor is listening

} catch(Exception e) {
    e.printStackTrace();
    System.exit(1);
}

[...]

try {
    myMBeanServer.invoke (discoveryResponderMBeanObjectName,
        "start", null, null) ;
} catch(Exception e) {
    echo("\tDiscoveryResponder MBean was already started.") ;
}

The discovery responder has attributes for exposing a multicast group and a multicast port. These attributes define a multicast socket that the responder will use to receive discovery requests. It will also send activation and deactivation messages to this multicast group. When sending automatic responses to discovery requests, the time-to-live is provided by the discovery client. The responder's time-to-live attribute is only used when sending activation and deactivation messages.

We use the default settings of the discovery responder that are the multicast group 224.224.224.224 on port 9000 with time-to-live of 1. In order to modify these values, you need to set the corresponding attributes before starting the discovery responder MBean. You can also specify them in the class constructor. If the responder is active, you will need to stop it before trying to set any of these attributes. In that way, it will send a deactivation message using the old values and then an activation message with the new values.

Discovery Monitor

The discovery monitor is a notification broadcaster: when it receives an activation or deactivation message from a discovery responder, it sends a discovery responder notification to its listeners. Once its parameters are configured and the monitor is activated, the discovery is completely passive. You can add or remove listeners at any time.

The DiscoveryMonitor MBean has multicast group and multicast port attributes that determine the multicast socket where it will receive responder messages. Like the other components of the discovery service, the default multicast group is 224.224.224.224 and the default port is 9000. You can specify other values for the group and port either in the constructor or through attribute setters when the monitor is off-line.

The discovery monitor in our example is registered as an MBean. We then add a listener through the MBean server as we would for any other notification broadcaster.


Example 17–4 Instantiating and Starting a Discovery Monitor

// build the DiscoveryMonitor MBean ObjectName
//
ObjectName discoveryMonitorMBeanObjectName =
    new ObjectName(domain + "name=myDiscoveryMonitor");

// Create, register and start the DiscoveryMonitor MBean
//
try {
    ObjectInstance discoveryMonitorObjectInstance =
        myMBeanServer.createMBean(
            "com.sun.jdmk.discovery.DiscoveryMonitor",
            discoveryMonitorMBeanObjectName) ;
    myMBeanServer.invoke (discoveryMonitorMBeanObjectName,
        "start", null, null);

} catch(Exception e) {
    e.printStackTrace();
    System.exit(1);
}

// Add ourselves as a listener to the DiscoveryMonitor MBean
//
try {
    myMBeanServer.addNotificationListener(
        discoveryMonitorMBeanObjectName, this, null, null ) ;

} catch(Exception e) {
    e.printStackTrace();
    System.exit(1);
}

The discovery monitor must be activated with the start operation before it will receive responder messages and send notifications. It will be stopped automatically if it is unregistered from its MBean server. If it is not used as an MBean, you should invoke its stop method before your application exits.

Discovery Responder Notifications

When it receives a responder's activation or deactivation message, the discovery monitor sends notification objects of the DiscoveryResponderNotification class. This notification contains the new state of the discovery responder (ONLINE or OFFLINE) and a DiscoveryResponse object with information from the agent where the responder is located.

The listener could use this information to update a list of agents in the network. In our example, the listener is the agent application itself, and the handler method only prints out the information in the notification.


Example 17–5 Discovery Responder Notification Handler

public void handleNotification(
    javax.management.Notification notification, java.lang.Object handback) {

  // We know we will only receive this subclass, so we can do the cast
  DiscoveryResponderNotification discoveryNotif =
        (DiscoveryResponderNotification) notification;
  echo("\n>>> RECEIVED Discovery Notification FROM JDMK agent on host \"" +
        discoveryNotif.getEventInfo().getHost() + "\"");

  if ( discoveryNotif.getState().intValue() == DiscoveryMonitor.ONLINE ) {
        echo("\t DiscoveryResponder state = ONLINE");
  } else {  
        echo("\t DiscoveryResponder state = OFFLINE");
  }
  DiscoveryResponse info =
        (DiscoveryResponse) discoveryNotif.getEventInfo();

  // internal method for displaying the discovery response information
  printDiscoveryResponse(info);
}

Running the Discovery Example

The examplesDir/Discovery directory contains the source file for the agent application that demonstrates the discovery service. This agent creates the discovery client, monitor and responder in the same MBean server, so it notifies itself when the responder is online and discovers its own presence. By starting several of these applications, you can see other agents being discovered.

Compile the file in this directory with the javac command. For example, on the Solaris platform with the Korn shell, type:


$ cd examplesDir/Discovery/
$ javac -classpath classpath *.java
How to Interact with the Discovery Example
  1. To run the discovery example, start the agent application with the following command:


    $ java -classpath classpath DiscoveryAgent
    

    The agent application registers an HTML adaptor on the default port (8082).

  2. Press Enter to initialize the discovery components.

    The discovery service is now ready, except for the discovery responder that has not been started yet.

  3. Press Enter a second time to activate the discovery responder.

    The discovery monitor that has already been started receives the responder's activation message and triggers the notification listener. Our handler method prints out the information from the discovery response notification, indicating that the responder is ONLINE.

  4. Press Enter again to invoke the findCommunicators method of the discovery client and display its results.

    The content of the discovery response is identical to the previous information, in particular, it contains the object name of the HTML adaptor in our application.

    Leave the application running in this state.

  5. Now start another DiscoveryAgent application in another terminal window or on another host. You must specify a non default port number for the HTML adaptor if running on the same host, for example:


    $ java -classpath classpath DiscoveryAgent 8084
    

    Both applications are using the multicast group 224.224.224.224 and multicast port 9000, so they will detect each other's presence. Go through the steps of this second application. The first application receives the activation message of the second responder. Then, the active discovery will detect both agents. If your agents are running on the same host, you can tell them apart by their unique MBeanServerId values in the response information.

  6. Before stopping either of the discovery responders, you can interact with them through their HTML adaptors. Connect to the first agent by loading the following URL in a browser:

    You can see the MBeans for all of the discovery components. From the MBean view of the discovery responder, call its stop operation, modify its TimeToLive attribute, and restart it. The discovery monitor in the other agent should detect the two events and signal them to our listener.

    You can also initiate an active search through the discovery client's MBean: invoke either of its find methods. However, the HTML adaptor cannot display the contents of the resulting DiscoveryResponse object.

  7. When you have finished, stop the discovery responders on both agents and then the applications themselves by pressing Enter twice in each terminal window.

    The second agent to be stopped will detect the deactivation of the first discovery responder.