Java Dynamic Management Kit 5.1 Tutorial

Chapter 21 Legacy Protocol Connectors

Protocol connectors provide a point-to-point connection between a Java dynamic management agent and a management application. Each of the connectors relies on a specific communication protocol, but the API that is available to the management application is identical for all connectors and is entirely protocol-independent.

A connector consists of a connector server component registered in the agent and a connector client object instance in the management application. The connector client exposes a remote version of the MBean server interface. Each connector client represents one agent to which the manager wants to connect. The connector server replies to requests from any number of connections and fulfills them through its MBean server. Once the connection is established, the remoteness of the agent is transparent to the management application, except for any communication delays.

From Java Dynamic Management Kit (Java DMK) 5.1 onwards, remote connections are established via the remote method invocation (RMI) and JMX messaging protocol (JMXMP) connectors defined by Java Management Extensions (JMX) Remote API. However, for reasons of backwards compatibility, the connectors used in previous versions of Java DMK are retained as legacy features of the product.

All the connectors rely on the Java serialization package to transmit data as Java objects between client and server components. Therefore, all objects needed in the exchange of management requests and responses must be instances of a serializable class. However, the data encoding and sequencing are proprietary, and the raw data of the message contents in the underlying protocol are not exposed by the connectors.

All the legacy connectors provided with Java DMK implement a heartbeat mechanism to detect automatically when a connection is lost. This enables the manager to be notified when the communication is interrupted and when it is restored. If the connection is not reestablished, the connector automatically frees the resources that were associated with the lost connection.

The code samples in this chapter are taken from the files in the legacy/SimpleClients and legacy/HeartBeat directories located in the main examplesDir (see Directories and Classpath in the Preface).

This chapter covers the following topics:

21.1 Legacy Connector Servers

Java DMK 5.0 introduced the concept of multihome interfaces. This enables you to work in an environment in which multiple network protocols can be used at different times by the legacy connector servers.

The Java DMK legacy connector servers on the server side do not need to have specified local interfaces. By default, the connector server listens on all local interfaces.

There is a legacy connector server for each of the protocols supported in the previous versions of Java DMK: RMI, hypertext transfer protocol (HTTP) and secure HTTP (HTTPS). Support for these protocols is retained in Java DMK 5.1 for reasons of backwards compatibility. All the legacy connectors inherit from the CommunicatorServer class that is the superclass for all the legacy protocol adaptors and connector servers. This class defines the methods needed to control the port and communication settings that are common to all. Each connector server class then provides specific controls, such as the service name for RMI and authentication information for HTTP. This example covers the legacy RMI connector server, and the HTTP authentication mechanism is described in Chapter 23, Legacy Connector Security.

A connector server listens for incoming requests from its corresponding connector client, decodes that request and encodes the reply. Several connector clients can establish connections with the same connector server, and the connector server can handle multiple requests simultaneously. There only needs to be one connector server MBean per protocol to which the agent needs to respond. However, several connector servers for the same protocol can coexist in an agent for processing requests on different ports.

21.1.1 Instantiating a Legacy RMI Connector Server

The legacy RMI connector server is an MBean, so we instantiate its class and register it in the MBean server. This operation could also be performed remotely, for example if a management application wants to access an agent through an alternative protocol.

The code extract shown in Example 21–1 is taken from the BaseAgent class in the examplesDir/current/BaseAgent directory.


Example 21–1 Instantiating the Legacy RMI Connector Server

// Instantiate an RMI connector server with default port
//
CommunicatorServer rmiConnector = new RmiConnectorServer();

try {
    // We let the RMI connector server provide its default name
    ObjectName rmiConnectorName = null;
    ObjectInstance rmiConnectorInstance =
        myMBeanServer.registerMBean( rmiConnector, rmiConnectorName );

} catch(Exception e) {
    e.printStackTrace();
}

Other constructors for the legacy RmiConnectorServer class have parameters for specifying the port and the RMI service name that the connector server will use. The default constructor assigns port 1099 and name=RmiConnectorServer, as given by the static variables RMI_CONNECTOR_PORT and RMI_CONNECTOR_SERVER, respectively, of the ServiceName class. Both attributes can also be accessed through the getter and setter methods of the legacy RmiConnectorServer class.

Each legacy connector uses different parameters that are specific to its protocol. For example, the HTTP connector does not need a service name. The default values for all parameters are given by the static variables of the ServiceName class.

Registering a connector server as an MBean implies that its MBean server will handle the remote requests that it receives. However, you can specify a different object for fulfilling management requests through the setMBeanServer method that the RmiConnectorServer class inherits from the CommunicatorServer class. For security reasons, this method is not exposed in the legacy RMI connector server MBean, so it must be called from the agent application.

Registering the connector server as an MBean is optional. For example, you might not want it exposed for management. In this case, you must use the setMBeanServer method to specify an object that implements the MBeanServer interface so that it can fulfill management requests.

A user can select a local server interface for a legacy RMI connector server using the RMI property java.rmi.server.hostname.

21.1.2 Connector States

Like all communicator servers, the legacy RMI connector server has a connection state that is identified by the static variables of the CommunicatorServer class:

All connector servers are OFFLINE after their instantiation, so they must be started explicitly. Then, you must wait for a connector server to come ONLINE before it can respond to connections on its designated port.


Example 21–2 Starting the RMI Connector Server

// Explicitly start the RMI connector server
//
rmiConnector.start();

// waiting for it to leave starting state...
while (rmiConnector.getState() == CommunicatorServer.STARTING) {
    try {
        Thread.sleep( 1000 );
    } catch (InterruptedException e) {
        continue;
    }
}

Instead of blocking the application thread, you can register a listener for attribute change notifications concerning the State attribute of the connector server MBean. All connector servers implement this notification which contains both old and new values of the attribute (see 8.3 Attribute Change Notifications). Listeners in the agent can then asynchronously detect when the state changes from STARTING to ONLINE.


Note –

During the STARTING state, the legacy RMI connector server registers its RMI service name with the RMI registry on the local host for its designated port. If no registry exists, one is instantiated for the given port. Due to a limitation of version 1.4 of the Java platform, creating a second registry in the same Java virtual machine (JVM) will fail. As a workaround, before starting an RMI connector server on a new, distinct port number in an agent, you must run the rmiregistry command from a terminal on the same host. This limitation is specific to the legacy RMI connector. The HTTP protocols do not require a registry.


The stop method is used to take a connector server offline. The stop method is also called by the preDeregister method that all connector servers inherit. Stopping a connector server interrupts all requests that are pending and closes all connections that are active. When a connection is closed, all of its resources are cleaned up, including all notification listeners, and the connector client can be notified by a heartbeat notification (see 21.3 Legacy Heartbeat Mechanism). A connection that is closed can no longer be reopened. The connector client must establish a new connection when the connector server is restarted.

The setPort method that all connector servers inherit from the CommunicatorServer class enables you to change the port on which management requests are expected. You can only change the port when the connector server is offline, so it must be explicitly stopped and then restarted. The same rule applies to the setServiceName method that is specific to the RMI connector server. These methods are also exposed in the MBean interface, along with start and stop, enabling a remote management application to configure the connector server through a different connection.

21.2 Legacy Connector Clients

The manager application interacts with a connector client to access an agent through an established connection. Through its implementation of the RemoteMBeanServer interface, a legacy connector client provides methods to handle the connection and access the agent. This interface specifies nearly all of the same methods as the MBeanServer interface, meaning that an agent is fully manageable from a remote application.

Through the connector, the management application sends management requests to the MBeans located in a remote agent. Components of the management application access remote MBeans by calling the methods of the connector client for getting and setting attributes and calling operations on the MBeans. The connector client then returns the result, providing a complete abstraction of the communication layer.

By default, a legacy connector client uses the default host name, InetAddress.getLocalHost().getHostName()

21.2.1 Multihome Interfaces

The multihome interfaces introduced in Java DMK 5.0 enable you to select the interfaces for the different network protocols used by legacy connector clients.

An HTTP(S) client enables you to select a local interface to receive notifications from a server. This user-specific local interface address is sent to a server and is then used by that server to push notifications. The interface address can be specified as either an IPv4 or an IPv6 address, or as a host name.

A legacy RMI connector client does not need to specify a local interface.

21.2.2 RemoteMBeanServer Interface

All connector clients implement the RemoteMBeanServer interface to expose the methods needed to access and manage the MBeans in a remote agent. This interface allows all management operations that would be possible directly in the agent application. In fact, the methods of the connector client for accessing MBeans remotely have exactly the same name and signature as their equivalent methods in the MBeanServer interface.

Table 21–1 lists the methods that are defined identically in both the MBeanServer and the RemoteMBeanServer interfaces.

Table 21–1 Shared Methods

Type 

Signature 

* void

addNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, java.lang.Object handback)

ObjectInstance

createMBean(*) – All four overloaded forms of this method

* java.lang.Object

getAttribute(ObjectName name, java.lang.String attribute)

* AttributeList

getAttributes(ObjectName name, java.lang.String[] attributes)

java.lang.String

getDefaultDomain()

java.lang.Integer

getMBeanCount()

* MBeanInfo

getMBeanInfo(ObjectName name)

ObjectInstance

getObjectInstance(ObjectName name)

* java.lang.Object

invoke(ObjectName name, java.lang.String operationName, java.lang.Object[] params, java.lang.String[] signature)

boolean

isInstanceOf(ObjectName name, java.lang.String className)

boolean

isRegistered(ObjectName name)

java.util.Set

queryMBeans(ObjectName name, QueryExp query)

java.util.Set

queryNames(ObjectName name, QueryExp query)

* void

removeNotificationListener(ObjectName name, NotificationListener listener)

* void

setAttribute(ObjectName name, Attribute attribute)

* AttributeList

setAttributes(ObjectName name, AttributeList attributes)

* void

unregisterMBean(ObjectName name)

These methods are defined in the ProxyHandler interface (see 24.1.1 Legacy Local and Remote Proxies).

Components of a management application that rely solely on this common subset of methods can be instantiated in either the agent or the manager application. Such components are location-independent and can be reused either locally or remotely as management solutions evolve. This symmetry also allows the design of advanced management architectures where functionality can be deployed either in the agent or in the manager, depending on runtime conditions.

The other, unshared methods of the RemoteMBeanServer interface are used to establish and monitor the connection. 21.2.3 Establishing a Connection shows how to establish a connection and access MBeans directly through the connector client.21.3 Legacy Heartbeat Mechanism shows how to monitor a connection and detect when it is lost.

21.2.3 Establishing a Connection

The target of a connection is identified by a protocol-specific implementation of the ConnectorAddress interface. This object contains all the information that a connector client needs to establish a connection with the target agent. All address objects specify a host name and port number. An RMI address adds the required service name, and HTTP-based addresses have an optional authentication field (see 23.1 Password-Based Authentication (Legacy Connectors)). In addition, the ConnectorType string identifies the protocol without needing to introspect the address object.

In our example, the target of the connection is an active legacy RMI connector server identified by an RmiConnectorAddress object. We use the default constructor to instantiate a default address object, but otherwise, these parameters can be specified in a constructor or through setter methods. The default values of the information contained in the RmiConnectorAddress object are the following:

The RmiConnectorAddress object is used as the parameter to the connect method of the RmiConnectorClient instance. This method tries to establish the connection and throws an exception if there is a communication or addressing error. Otherwise, when the connect method returns, the connector client is ready to perform management operations on the designated agent.

The code segment shown inExample 21–3 is taken from the ClientWithoutProxy class in the examples directory.


Example 21–3 Establishing a Connection

echo("\t>> Instantiate the RMI connector client...");
connectorClient = new RmiConnectorClient();

echo("\t>> Instantiate a default RmiConnectorAddress object...");
RmiConnectorAddress address = new RmiConnectorAddress();

// display the default values
echo("\t\tTYPE\t= "   + address.getConnectorType());
echo("\t\tPORT\t= "   + address.getPort());
echo("\t\tHOST\t= "   + address.getHost());
echo("\t\tSERVER\t= " + address.getName());
echo("\t<< done <<");

echo("\t>> Connect the RMI connector client to the agent...");
try {
     connectorClient.connect( address );

} catch(Exception e) {
     echo("\t!!! RMI connector client could not connect to the agent !!!");
     e.printStackTrace();
     System.exit(1);
}

21.2.4 Managing MBeans Remotely

Once the connection to an agent is established, the management application can access that agent's MBeans through the legacy RemoteMBeanServer interface of the connector client. Invoking these methods has exactly the same effect as calling the equivalent methods directly on the MBean server instance.

It is possible to restrict access to certain methods of the MBean server when they are called through the legacy connector client, but this is performed by a security mechanism in the legacy connector server. See 23.2 Context Checking for more details.

21.2.4.1 Creating and Unregistering MBeans in the Agent

We use the createMBean method to instantiate and register an object from its class name. This class must already be available in the agent application's classpath.


Example 21–4 Creating and Unregistering an MBean Remotely

private void doWithoutProxyExample(String mbeanName) {

    try {

        // build the MBean ObjectName instance
        //
        ObjectName mbeanObjectName = null;
        String domain = connectorClient.getDefaultDomain();
        mbeanObjectName = new ObjectName( domain + ":type=" + mbeanName );

        // create and register an MBean in the MBeanServer of the agent
        //
        echo("\nCurrent MBean count in the agent = "+
             connectorClient.getMBeanCount());
        echo("\n>>> CREATE the " + mbeanName +
             " MBean in the MBeanServer of the agent:");
        String mbeanClassName = mbeanName;

        ObjectInstance mbeanObjectInstance =
        connectorClient.createMBean( mbeanClassName, mbeanObjectName );

        echo("\tMBEAN CLASS NAME      = " +
             mbeanObjectInstance.getClassName() );
        echo("\tMBEAN OBJECT NAME     = " +
             mbeanObjectInstance.getObjectName() );
        echo("\nCurrent MBean count in the agent = "+
             connectorClient.getMBeanCount() );

        [...] // Retrieve MBeanInfo and access the MBean (see below)

        // unregister the MBean from the agent 
        //
        echo("\n>>> UNREGISTERING the "+ mbeanName +" MBean");
        connectorClient.unregisterMBean(
            mbeanObjectInstance.getObjectName() );

        [...]

    } catch (Exception e) {
        e.printStackTrace();
    }
}

Example 21–4 shows the use of other calls to the remote agent, such as getDefaultDomain and getMBeanCount that have the same purpose as in an agent application.

21.2.4.2 Accessing MBean Attributes and Operations

Once you can access the object names for MBeans in the agent, you can know their management interface from their MBeanInfo object. The code in Example 21–5 is actually called in between the MBean creation and unregistration shown in Example 21–4.


Example 21–5 Retrieving the MBeanInfo Object

ObjectName mbeanObjectName = mbeanObjectInstance.getObjectName();

echo("\n>>> Getting the management information of the MBean");
MBeanInfo info = null;
try {

    info = connectorClient.getMBeanInfo( mbeanObjectName );

} catch (Exception e) {
    echo("\t!!! Could not get MBeanInfo object for "+ mbeanObjectName );
    e.printStackTrace();
    return;
}

// display content of MBeanInfo object
//
echo("\nCLASSNAME: \t"+ info.getClassName());
echo("\nDESCRIPTION: \t"+ info.getDescription());
echo("\nATTRIBUTES");
MBeanAttributeInfo[] attrInfo = info.getAttributes();
if ( attrInfo.length>0 ) {
    for( int i=0; i<attrInfo.length; i++ ) {
        echo(" ** NAME: \t"+ attrInfo[i].getName());
        echo("    DESCR: \t"+ attrInfo[i].getDescription());
        echo("    TYPE: \t"+ attrInfo[i].getType() +
             "\tREAD: "+ attrInfo[i].isReadable() +
             "\tWRITE: "+ attrInfo[i].isWritable());
    }
} else echo(" ** No attributes **");

[...]

It is then straightforward to perform management operations on MBeans through the legacy connector client. As in an agent, we call the generic getters, setters, and call methods with the object name of the MBean, the name of the attribute or operation, and any parameters. As in the methods of the MBean server, we need to use the Attribute and AttributeList classes to pass attributes as name-value pairs.


Example 21–6 Accessing an MBean Through the Legacy Connector Client

try {
    // Getting attribute values
    String State = (String)
        connectorClient.getAttribute( mbeanObjectName, "State" );
    Integer NbChanges = (Integer)
        connectorClient.getAttribute( mbeanObjectName, "NbChanges" );
    echo("\tState     = \"" + State + "\"");
    echo("\tNbChanges = " + NbChanges);

    // Setting the "State" attribute
    Attribute stateAttr = new Attribute("State", "new state from client");
    connectorClient.setAttribute(mbeanObjectName, stateAttr);

    // Invoking the "reset" operation
    Object[] params = new Object[0];
    String[] signature = new String[0];
    connectorClient.invoke(mbeanObjectName, "reset", params, signature);

} catch (Exception e) {
    e.printStackTrace();
    return;
}

All other MBean access methods are available in the same manner, such as bulk getters and setters, and the query methods.

21.2.4.3 Creating and Accessing Dynamic MBeans

In the first run of the example, the management application creates, manages, and unregisters a standard MBean in the remote agent. However, standard and dynamic MBeans are designed to be managed through the same methods, both in the MBeanServer and in the RemoteMBeanServer interfaces.

As shown in Example 21–4, the subroutine of the example application takes only a single class name parameter. The first time this subroutine is called, the example passes the class name of a standard MBean, and the second time, that of a dynamic MBean. For the example to run, the two MBeans must have an identical management interface. By extension of this special case, we see that the connector client can manage dynamic MBeans through the same methods as it manages standard MBeans, without making any distinction between the two.

21.2.5 Running the SimpleClients Example

The examplesDir/legacy/SimpleClients directory contains all of the files for three sample managers, a base agent, and some MBeans to manage. In this chapter, we run the ClientWithoutProxy application that demonstrates simple operations on MBeans through a legacy RMI connector.

To Run the Simple Client Example
  1. Compile all files in this directory with the javac command.

    For example, on the Solaris platform with the Korn shell, type:


    $ cd examplesDir/legacy/SimpleClients/
    $ javac -classpath classpath *.java
    

    We do not need all the files for this chapter, but they are used in Chapter 24, Legacy Proxy Mechanism. In this demonstration, we only need the BaseAgent and ClientWithoutProxy applications, as well as the standard and dynamic MBeans.

  2. Start the agent in another terminal window on the same host with the following command:


    $ java -classpath classpath BaseAgent
    

    The agent creates an HTML protocol adaptor and a legacy RMI connector server to which the client application establishes a connection, and then it waits for management operations.

  3. Wait for the agent to be completely initialized, then start the manager with the following command:


    $ java -classpath classpath ClientWithoutProxy
    

    The client application creates the RMI connector client and establishes the connection to the base agent.

  4. Press Enter in the manager window to step through the example.

    The management application instantiates both types of MBeans, looks at their metadata, and performs management operations on them. The results of each step are displayed in the terminal window.

  5. At any time, you can view the agent through its HTML adaptor and interact with the MBeans created by the management application.

    For example, immediately after the manager creates an MBean, you could modify its attributes and see this change reflected when the connector client access the new values.

  6. Press Enter in both windows to stop the agent and manager applications.

21.3 Legacy Heartbeat Mechanism

The legacy heartbeat mechanism monitors the connection between a manager and an agent and automates the cleanup procedure when the connection is lost. This enables both the manager and the agent to release resources that were allocated for maintaining the connection.

The mechanism is entirely contained in the connector client and connector server components, no additional objects are involved. In addition, connector clients send notifications that the manager application can receive to be aware of changes in the status of a connection.

All legacy connector clients of the Java DMK implement the HeartBeatClientHandler interface to provide a heartbeat mechanism. This means that agent-manager connections over the legacy RMI, HTTP, and HTTPS connectors can be monitored and controlled in the same way. A manager application could even use the same notification handler for all connector clients where the heartbeat mechanism is activated.

21.3.1 Configuring the Heartbeat

To monitor the connection, the connector client sends periodic heartbeats (ping requests) to the connector server that acknowledges them by sending a reply (ping responses). If either heartbeat is lost, the components of the legacy connector retry until either the connection is re-established or the number of retries has been exhausted.

In a connector client, the methods specified by the HeartBeatClientHandler interface set the heartbeat period and the number of retries that are attempted. You should determine these parameters empirically to implement the desired connection monitoring behavior, taking into account the network conditions and topology between the hosts of your manager and agent applications.

In Example 21–7, the management application configures the heartbeat mechanism before the connection to an agent is established.


Example 21–7 Configuring the Heartbeat in the Connector Client

// CREATE a new RMI connector client
//
echo("\tInstantiate the RMI connector client...");
connectorClient = new RmiConnectorClient();

// SET heartbeat period to 1 sec. Default value is 10 secs
//
echo("\tSet heartbeat period to 1 second...");
connectorClient.setHeartBeatPeriod(1000);

// SET heartbeat number of retries to 2. Default value is 6 times
//
echo("\tSet heartbeat number of retries to 2 times...");
connectorClient.setHeartBeatRetries(2);

Using the same methods, the heartbeat configuration can also be modified at any time, even after the connection has been established. By default, the heartbeat mechanism is activated in a connector with a 10 second heartbeat and 6 retries, meaning that a connection that cannot be re-established within one minute is assumed to be lost.

Setting the number of heartbeat retries to zero causes a lost connection to be signalled immediately after the heartbeat fails. Setting the heartbeat period to zero deactivates the mechanism and prevents any further connection failures from being detected.

No specific configuration is necessary on the agent-side legacy connector server. It automatically responds to the heartbeat messages. These heartbeat messages contain the current heartbeat settings from the legacy connector client that also configure the connector server. In this way, both client and server apply the same retry policy, and when the configuration is updated in the connector client, it is immediately reflected in the connector server. The connector server can handle multiple connections from different management applications, each with its specific heartbeat configuration.

The legacy connector server applies its retry policy when the next expected heartbeat message is not received within the heartbeat period. From that moment, the connector server begins a timeout period that lasts 20% longer than the number of retries times the heartbeat period. This corresponds to the time during which the connector client attempts to resend the heartbeat, with a safety margin to allow for communication delays. If no further heartbeat is received in that timeout, the connection is determined to be lost.

The heartbeat ping messages also contain a connection identifier so that connections are not erroneously re-established. If a connector server is stopped, thereby closing all connections, and then restarted between two heartbeats or before the client's timeout period has elapsed, the server responds to heartbeats from a previous connection. However, the connector client detects that the identifier has changed and immediately declares that the connection is lost, regardless of the number of remaining retries.

During the timeout period, the notification push mechanism in the connector server is suspended to avoid losing notifications (see Chapter 22, Notification Forwarding in Legacy Connectors). Similarly, while the connector client is retrying the heartbeat, it must suspend the notification pull mechanism if it is in effect.

When a connection is determined to be lost, both the connector client and server free any resources that were allocated for maintaining the connection. For example, the connector server unregisters all local listeners and deletes the notification cache needed to forward notifications. Both components also return to a coherent, functional state, ready to establish or accept another connection.

The state of both components after a connection is lost is identical to the state that is reached after the disconnect method of the connector client is called. In fact, the disconnect method is called internally by the connector client when a connection is determined to be lost, and the equivalent, internal method is called in the connector server when its timeout elapses without recovering a heartbeat.

21.3.2 Receiving Heartbeat Notifications

The legacy connector client also sends notifications that signal any changes in the state of the connection. These notifications are instances of the HeartBeatNotification class. The HeartBeatClientHandler interface includes methods specifically for registering for heartbeat notifications. These methods are distinct from those of the NotificationRegistration interface that a connector client implements for transmitting agent-side notifications (see 22.1 Registering Manager-Side Listeners).


Example 21–8 Registering for Heartbeat Notifications

// Register this manager as a listener for heartbeat notifications
// (the filter and handback objects are not used in this example)
//
echo("\tAdd this manager as a listener for heartbeat notifications...");
connectorClient.addHeartBeatNotificationListener(this, null, null);

Instances of heartbeat notifications contain the connector address object from the connection that generated the event. This enables the notification handler to listen to any number of connectors and retrieve all relevant information about a specific connection when it triggers a notification. The HeartBeatNotification class defines constants to identify the possible notification type strings for heartbeat events:

Once they are established, connections can go through any number of retrying/re-established cycles and then be terminated by the user or determined to be lost and terminated automatically. When the heartbeat mechanism is deactivated by setting the heartbeat period to zero, only heartbeat notifications for normally established and normally terminated connections continue to be sent. In that case, connections can be lost but they are not detected nor indicated by a notification.

The following diagram shows the possible sequence of heartbeat notifications during a connection. Retries are enabled when the getHeartBeatRetries method returns an integer greater than zero.

Figure 21–1 Sequencing of Heartbeat Notifications

Diagram showing sequencing of heartbeat notifications

Example 21–9 shows the source code for the notification handler method in our manager class. The handler prints out the notification type and the RMI address associated with the connector that generated the notification.


Example 21–9 Heartbeat Notification Handler

public void handleNotification(Notification notification, Object handback){

    echo("\n>>> Notification has been received...");
    echo("\tNotification type = " + notification.getType());

    if (notification instanceof HeartBeatNotification) {
        ConnectorAddress notif_address =
            ((HeartBeatNotification)notification).getConnectorAddress();

        if (notif_address instanceof RmiConnectorAddress) {
            RmiConnectorAddress rmi_address =
                (RmiConnectorAddress) notif_address;

            echo("\tNotification connector address:");
            echo("\t\tTYPE   = " + rmi_address.getConnectorType());
            echo("\t\tHOST   = " + rmi_address.getHost());
            echo("\t\tPORT   = " + rmi_address.getPort());
            echo("\t\tSERVER = " + rmi_address.getName());
	        }
    }
}

In the agent application, the connector server does not emit any notifications about the state of its connections. The HTTP protocol-based connectors do provide a count of active clients, but there is no direct access to heartbeat information in an agent's connector servers.

21.3.3 Running the Legacy Heartbeat Example

The examplesDir/legacy/HeartBeat directory contains all of the files for the Agent and Client applications that demonstrate the heartbeat mechanism through an RMI connector.

To Run the Heartbeat Example: Normal Termination
  1. Compile all files in this directory with the javac command.

    For example, on the Solaris platform with the Korn shell, type:


    $ cd examplesDir/legacy/HeartBeat/
    $ javac -classpath classpath *.java
    

    To demonstrate the various communication scenarios, we will run the example three times: once to see a normal termination, once to see how the manager reacts to a lost connection, and once to see how the agent reacts to a lost connection.

  2. Start the agent on another host or in another terminal window with the following command:


    $ java -classpath classpath Agent
    

    The agent only creates the legacy RMI connector server to which the client application will establish a connection, and then it waits for management operations.

  3. Wait for the agent to be completely initialized, then start the manager with the following command, where hostname is the name of the host running the agent.

    The RMI connector in this example uses port 1099 by default. If you started the agent on the same host, you can omit the hostname and the port number:


    $ java -classpath classpath Client hostname 1099
    

    The client application creates the legacy RMI connector client, configures its heartbeat, and registers a notification listener, as seen in the code examples. When the connection is established, the listener outputs the notification information in the terminal window.

  4. Press Enter in the manager window to call the disconnect method on the connector client and stop the Client application.

    In the terminal window, the heartbeat notification listener outputs the information for the normal connection termination before the application ends.

  5. Leave the agent application running for the next scenario.

To Run the Legacy Heartbeat Example: Connector Client Reaction
  1. Start the Client application again:


    $ java -classpath classpath Client hostname 1099
    
  2. When the connection is established, press Control-C in the agent's window to stop the connector server and the agent application.

    This simulates a broken communication channel as seen by the connector client.

    Less than a second later, when the next heartbeat fails, the heartbeat retrying notification is displayed in the manager's terminal window. Two seconds later, after both retries have failed, the lost connection and terminated connection notifications are displayed.

  3. Press Enter in the manager window to exit the Client application.

To Run the Legacy Heartbeat Example: Connector Server Reaction
  1. Start the agent in debug mode on another host or in another terminal window with the following command:


    $ java -classpath classpath -DLEVEL_DEBUG Agent
    
  2. Wait for the agent to be completely initialized, then start the Client application again:


    $ java -classpath classpath Client hostname 1099
    

    When the connection is established, the periodic heartbeat messages are displayed in the debug output of the agent.

  3. This time, press Control-C in the client's window to stop the connector client and the manager application.

    This simulates a broken communication channel as seen by the connector server in the agent.

    After the heartbeat retry timeout elapses in the agent, the lost connection message is displayed in the heartbeat debugging output of the agent.

  4. Type Control-C in the agent window to stop the agent application and end the example.