Java Dynamic Management Kit 5.1 Tutorial

Chapter 14 Cascading Service

The cascading service enables you to access the MBeans of a subagent directly through the MBean server of a master agent. The cascading service has been completely overhauled in Java Dynamic Management Kit (Java DMK) 5.1 to allow it to operate over the connector protocols defined by the Java Management Extensions (JMX) Remote API. The legacy cascading service is now deprecated. The examples of the legacy cascading service have been retained in Chapter 25, Legacy Cascading Agents, for reasons of backwards compatibility. However, when using legacy Java DMK connectors, you should use the new CasdingServiceMBean with wrapped legacy connectors rather than relying on the deprecated legacy cascading agent API.

The service is implemented in a CascadingServiceMBean, which makes it possible to mount source MBean servers (that are possibly located in subagents) into a target MBean server (that is located in a Master Agent) in a manner that is somewhat analogous to a File System mount operation. Several source MBean servers can be mounted in the same target MBean server, provided that different target paths are used. A source MBean server mounted from a subagent can itself be a Master Agent in which source MBean servers from another level of subagents are mounted, and so on and so on, forming a hierarchy of cascading agents.

By connecting to the root of an agent hierarchy, managers can have a single access point to many resources and services. All MBeans in the hierarchy are manageable through the top master agent, and a manager does not need to worry about their physical location. Like the other services, the cascading service is implemented as an MBean that can be managed dynamically. This enables the manager to control the structure of the agent hierarchy, adding and removing subagents as necessary.

In particular, the cascading service MBean can work with any protocol connector, including any custom implementation. Those supplied by the Java DMK give you the choice of using remote method invocation (RMI), or the JMX messaging protocol (JMXMP). The legacy connector protocols implemented by previous versions of Java DMK can also be used, as long as they have been wrapped for use with the JMX Remote API, as described in 9.5 Wrapping Legacy Connectors.

The cascading service also lets you specify a filter for selecting precisely the source MBeans that are cascaded in the target MBean server. This mechanism lets you limit the number of MBeans that are cascaded in the top agent of a large cascading hierarchy. For a general overview of the cascading service, see the Java Dynamic Management Kit 5.1 Getting Started Guide.

The code samples in this topic are from the files in the current/Cascading directory located in the main examplesDir (see Directories and Classpath in the preface).

This chapter contains the following topics:

14.1 CascadingService MBean

You should create a maximum of one CascadingService MBean in any target MBean server. The same CascadingService MBean can be used to mount multiple source MBean servers, that are located in multiple subagents in the Master Agent's target MBean server. The CascadingService creates one ProxyCascadingAgent behind the scenes for every mount operation it performs. The creation of that ProxyCascadingAgent is entirely hidden from the user. The complexity of the ProxyCascadingAgents is thus hidden by the CascadingService MBean. You should not attempt to use CascadingAgents directly, as was the case in earlier versions of Java DMK. Applications should always use the CascadingServiceMBean instead. The CascadingServiceMBean handles connections to the subagents and cascades all of that agent's registered MBeans into the master agent's target MBean server. No other classes are required or need to be generated to represent the MBeans.

The agent whose MBean server contains an active cascading service is called a master agent in relation to the subagent that is cascaded. The MBean server in which the MBeans are cascaded is called the target MBean server. An agent to which the cascading service is connected is called a subagent in relation to its master agent. Its MBean server is named the source MBean server. The source MBean server is the MBean server in which the MBeans actually reside. The CascadingService MBean creates target MBeans in the target MBean server to represent the subagent's source MBeans. For each source MBean, a CascadingProxy is registered behind the scenes in the target MBean server. See 14.2 Cascaded MBeans in the Master Agent for a description of these objects. The operation that makes it possible to cascade a source MBean server located in a subagent to the target MBean server in the Master Agent is called a mount operation, because of its similarity to a file system mount operation.

A master agent can have any number of subagents, each controlled individually by the same CascadingService MBean. Each mount operation is in fact implemented behind the scenes through a different CascadingAgent object created by the CascadingService MBean. The complexity of the CascadingAgent API is however completely abstracted by the CascadingServiceMBean. A subagent can itself contain cascading agents and cascaded MBeans, mounted from another layer of subagents, all of which will appear to be cascaded again in the master agent. This effectively enables cascading hierarchies of arbitrary depth and width.

Two master agents can also connect to the same subagent. This is similar to the situation where two managers connect to the same agent and can access the same MBean. If the implementation of a management solution permits such a condition, it is the designer's responsibility to handle any synchronization issues in the MBean.

The connection between two agents resembles the connection between a manager and an agent. The cascading service MBean relies on a connector client, and the subagent must have the corresponding connector server. The subagent's connector server must already be instantiated, be registered with its MBean server, and be ready to receive connections. The CascadingServiceMBean mount operation accepts a JMXServiceURL and a Map from which it obtains a JMXConnector from the JMXConnectorFactory. The mount operation will create the connection, and the unmount operation will close it.


Example 14–1 Mounting MBeans from a Subagent

[...]

private void mountSubagents(JMXServiceURL[] agentURLs) {
		 mountPoints = new String[agentURLs.length];
	    for (int i=0;i<agentURLs.length;i++) {
	         try {
               final String mountPointID =
		               cascadingService.mount(agentURLs[i],null,
                                         new ObjectName("ExportDomain:*"),
                                         "subagents/agent"+(i+1));

				     mountPoints[i] = mountPointID;
     		     echo(mountPointID);
	          } catch (Exception x) {
		           echo("\t!!! Could not mount subagent#"+(i+1)+" at " + 
		                agentURLs[i] + "\n\tError is: " + x);
		           x.printStackTrace();
		           echo("\nEXITING...\n");
		           System.exit(1);
	          }
	    }
}

	[...]

In Example 14–1, source MBean servers located in subagents are mounted using a defined JMXServiceURL, to cascade remote MBeans from the domain ExportedDomain. By default, all MBeans of the subagent are cascaded in the master agent, but in this example we provide an object name pattern so as only to select those in ExportedDomain. Example 14–1 is taken from the example class MasterAgent, in the directory examplesDir/current/Cascading. To emulate file system mount operations, a different target path for each mounted subagent is used. In this example, we use subagents/agent#x, where #x starts at 1, x is incremented for each subagent, and preserves the order of the JMX Service URLs that are given as input.


Note –

You should use a different target path for every mount operation. The Java DMK cascading service implementation does not enforce this rule, but applications that are concerned with naming coherency should not break it.



Example 14–2 Creating the Cascading Service

[...]

    private void createCascadingService() {

        printline();
        echo("Creating the CascadingService" +
             " MBean within the MBeanServer:");
        try {
	            CascadingService service  = new CascadingService();
             ObjectInstance   cascadingInstance =
                  server.registerMBean(service, null);
             echo("\tCLASS NAME  = " + cascadingInstance.getClassName());
             echo("\tOBJECT NAME = " + cascadingInstance.getObjectName());
	             cascadingService = service;
          } catch (Exception e) {
             echo("\t!!! Could not create the " +
                  CascadingService.class.getName() + " MBean !!!");
             e.printStackTrace();
             echo("\nEXITING...\n");
             System.exit(1);
          }
    }

[...]

As shown in Example 14–2, before the subagent's source MBean servers can be mounted, the CascadingService MBean must be created and registered in, or tied to, the master agent's target MBean server. The CascadingService MBean in this example is registered in the master agent's target MBean server with a null object name, so that it is registered with its default ObjectName, that is defined in the CascadingServiceMBean interface.


Example 14–3 Unmounting MBeans from a Subagent

[...]

private void unmountAll() {
      printline();

	      echo("Unregistering CascadingServiceMBean");
       try {
	          server.unregisterMBean(CascadingServiceMBean.
				                        CASCADING_SERVICE_DEFAULT_NAME);
	      } catch (Exception x) {
	          echo("\t!!! Could not unregister " + CascadingServiceMBean.
		            CASCADING_SERVICE_DEFAULT_NAME + "\n\tError is: " + x);
	          x.printStackTrace();
	          echo("\nEXITING...\n");
	          System.exit(1);
       } 

	       echo("Unmounting all mount points");

       final String mounts[] = cascadingService.getMountPointIDs();
	       for (int i=0;i<mounts.length;i++) {
	            try {
		              if (cascadingService.unmount(mounts[i]))
		                  echo("unmounted "+mounts[i]);
	            } catch (Exception x) {
		              echo("\t!!! Could not unmount " + mounts[i] +
		                   "\n\tError is: " + x);
		              x.printStackTrace();
		              echo("\nEXITING...\n");
		              System.exit(1);
	            }
	       }
  }

[...]

When the master agent is stopped, it stops the cascading service by unmounting all the subagents that are currently mounted. This is done by first unregistering the CascadingServiceMBean from its target MBean server, to ensure that nobody can create new mount points while the removal is ongoing. Then, for each currently mounted mount point, the unmount operation is called, so that all proxies for the associated cascaded MBeans are unregistered from the target MBean server. The MBean server delegate in the master agent sends an unregistration notification for each cascaded MBean as it is removed.

14.2 Cascaded MBeans in the Master Agent

Once the the cascading service has mounted a source MBean server in the target MBean server, you can interact directly through the target MBean server with the target MBeans representing the subagent's MBeans. You can access and manage these MBeans as if you were connected to the subagent and accessing or managing the original MBeans. The target MBeans that you interact with are in fact CascadingProxy MBean objects registered in the master agent's target MBean server with the same object name as the source MBean, but with a domain that is prefixed by the target path used in the mount operation.

All management operations that you can perform on the original MBean can be performed identically through the target MBeanServer, using that target's object name. You can modify attributes, invoke operations and add or remove listeners, all with exactly the same result as if the manager were connected to the subagent when performing the action.

The behavior of a target CascadingProxy MBean is to transmit the action transparently to the subagent's MBean and return with an answer or result. The actual computation is performed by the original MBean running in its own source MBean server.

In Example 14–4, there is a timer MBean that was created in the subagent. Once the subagent has been mounted in the target MBean server, the timer is operated through its local target CascadingProxy MBean. It is not possible to have the direct reference to a source MBean, but it is possible to obtain a local proxy using the MBeanServerInvocationHandler class, as if it were a local MBean registered in the target MBean server. The fact that the operations are actually routed to a subagent through a JMXConnector remains hidden.


Example 14–4 Managing Cascaded MBeans

    private void doCascadingOperations() {

        echo("\nPress Enter to continue...\n");
        waitForEnterPressed();
        printline();
        try {
             if (! cascadingService.isMounted(mountPoints[0])) {
		            echo(mountPoints[0] + ": \n\t" + "not mounted! ");
		            echo("Cannot do cascading operations.");
		            return;
	            }
            
             ObjectName timerName =
                new ObjectName("subagents/agent1/ExportDomain:type=Timer");
	             echo(">>> Get Timer MBean \""+timerName+"\"");
             TimerMBean timer = (TimerMBean) MBeanServerInvocationHandler.
               newProxyInstance(server, timerName, TimerMBean.class, true);

            echo("\n>>> Ask the Timer MBean to send a " +
                 "Timer Notification every 3 seconds");
            Date currentDate = new Date();
                  
            timer.addNotification("Timer","Message",null,
                                  new Date(currentDate.getTime() + 
                                  2),3000);

            timer.start();

            echo("\n>>> Add listener to the Timer MBean");
            server.addNotificationListener(timerName, this, null, null);
            echo("\tListener added successfully");
            echo("\nPress Enter to remove the listener from the " +
		             "Timer MBean...");
            waitForEnterPressed();

	    	      if (cascadingService.isMounted(mountPoints[0])) {
		             try {
                    server.removeNotificationListener(timerName, 
                    this);
		             } catch (Exception x) {
                    echo("Unexpected exception while unregistering 
                         listener:");
		                 echo("\tError is: " + x);
		             }
	            } else {
                 echo(mountPoints[0] + ": \n\t" + "already 
                      unmounted! ");
	            }

	        } catch (Exception e) {

            e.printStackTrace();
            System.exit(1);
        }
    }

Example 14–4 obtains a TimerMBean proxy for the Timer MBean from the subagent #1 ExportedDomain, and then performs operations on it. In particular, it registers for timer notifications and starts the timer.

To the managing application, the target MBean in the master agent target MBean server is the MBean. Unregistering a cascaded MBean in the master agent will not unregister the cascaded MBean in the subagent, but will only unregister the CascadingProxy from the target MBean server. You should not attempt to unregister MBeans mounted from a source MBean server through the target MBean server. If you want to create or unregister MBeans in a subagent, you should not do so through the CascadingService, but should use a JMXConnector directly connected to the subagent.

The cascading is almost totally transparent. A manager has no direct way of knowing whether an object is a cascaded object or not. It can attempt to guess the topology of the cascading hierarchy by examining the domain path of the object names, provided that the subagents have been mounted using a meaningful and coherent target path, but the underlying connectivity is completely transparent. The managing application can however be informed of failed or closed mount points by registering for notifications with the CascadingServiceMBean.

Likewise, some operations might fail due to the fact that the CascadingProxy object registered in the target MBean server is not the real source MBean. For example, unregistering the CascadingProxy will not cause the original source MBean to be unregistered. The CascadingProxy might reappear later if an external event makes the CascadingService update its view of the subagents. Similarly, trying to call addNotificationListener(ObjectName,ObjectName,...) with the object name of a target CascadingProxy as listener (namely the second parameter) will fail, because this operation cannot be routed to the source MBean.

14.2.1 Class of a Cascaded MBean

Proxy MBeans registered in the target MBeanServers when mounting source MBean servers are implemented as instances of the CascadingProxy class. The CascadingProxy MBean is locally registered in the target MBean server, and has the same MBeanInfo as its source MBean. In particular, if the source MBean is a model MBean, the MBeanInfo exposed by its CascadingProxy is ModelMBeanInfo. The exposed MBean information also contains the class name of the original MBean, and not the class name of the CascadingProxy. Exposing this borrowed MBeanInfo guarantees that the cascading service is as transparent as possible.

The symmetry of the Java dynamic management architecture means that this cascading mechanism is scalable to any number of levels. The cascaded object of a cascaded object is again an instance of the CascadingProxy class, and it borrows the same MBeanInfo. Any operation on the top target object is propagated to its source subagent, where the intermediate cascaded object will send it to its own source subagent, and so forth. The cost of cascading is the cost of accessing the subagents. The depth of your cascading hierarchy should be adapted to your management solution.

Because the cascading service MBean instantiates and controls all cascaded MBeans, the CascadingProxy class should never be instantiated through a management operation, nor by the code of the agent application. It is described here only to provide a better understanding of the internal behavior of the cascading service.

14.2.2 Cascading Issues

In this section, we explain some of the design issues that are determined by the implementation of the cascading service.

14.2.2.1 Dynamic Cascading

When an MBean is unregistered from the subagent, the cascading service automatically removes its corresponding target CascadingProxy MBean from the master agent's target MBean server. When a new MBean is registered in the subagent's source MBean server, the cascading service registers a new CascadingProxy mirroring this source MBean with the master agent's MBean server. Note that if an ObjectName pattern was provided when performing the mount operation, only those source MBeans whose source ObjectName satisfy that ObjectName pattern are considered.

Both these mechanisms scale to cascading hierarchies. Adding or removing an MBean in the subagent will trigger a notification that any cascading service connected to the subagent will receive. This will start a chain reaction up to the top of the hierarchy. Removing an MBean from the middle of a hierarchy also triggers a similar reaction up to the top target MBean, which is finally removed. However, as explained in the previous section, corresponding source MBeans that are lower in the hierarchy will not be affected. The cascading service reflects the content of a source MBean server into a target MBean server, but it cannot be used to create or remove MBeans in the source MBean server. Calls to createMBean, registerMBean, and unregisterMBean only affect the local target MBean server.

14.2.2.2 MBean Patterns and Filtering

When the cascading service MBean is instantiated, you can pass it an object name pattern. This object name pattern is applied to the list of MBeans in the subagent's source MBean server to determine those MBeans that are to be cascaded into the master agent's target MBean server. The filtering is activated for the life of the of the mount operation, namely, until unmount is called for that mount point. Care must be taken when using the ObjectName pattern, so as not to implement cascading hierarchies that are not analogous to File System mount operations.

The object name pattern is used to filter any new MBean that is registered in the source MBean server. If the new MBean meets the filter criteria, it will become visible and be cascaded into the target MBean server.

14.2.2.3 Using Target Paths

Although the API also allows you to implement different cascading schemes, your applications should only implement those schemes that can be compared to a regular File System mount, as follows.

The present implementation does not enforce those rules, but applications that are concerned with naming consistency and coherency should make sure to respect them.

14.3 Running the Cascading Example

The examplesDir/current/Cascading directory contains all of the files for the master agent and subagent applications, along with a simple MBean.

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


    $ cd examplesDir/current/Cascading/
    $ javac -classpath classpath *.java
    
  2. Start the subagent in another terminal window with the following command. Be sure that the classes for the SimpleStandard MBean can be found in its classpath.


    $ java -classpath classpath -Durl="subagent_input_url" SubAgent
    

    Here, subagent_url is the URL that is passed to the JMXConnectorServerFactory to create the subagent's JMXConnectorServer. You can use one of the following URLs:

    • service:jmx:jmxmp://

    • service:jmx:rmi://

    • service:jmx:iiop://

    Alternatively, you can specify your own URL, for example.


    service:jmx:jmxmp://host_name:port_number
    

    Note that you can start many subagents in different terminal windows, for example.


    $ java -Dhtml.port=8082 -Durl="subagent1_input_url" SubAgent
    $ java -Dhtml.port=8182 -Durl="subagent2_input_url" SubAgent
    ...

    However you start the subagent, it will inform you onscreen of the URL, subagent-actual-url, at which it can be found.

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


    $ java -classpath classpath MasterAgent subagent-actual-url
    

    In the command above, subagent-actual-url is the URL you were given when you launched the subagent. If you started more than one subagent, you can start the MasterAgent as shown in the following command.


    $ java -classpath classpath MasterAgent subagent1-actual-url 
    subagent2-actual-ur ... 
    

    When started, the master agent application first creates the CascadingService MBean and then mounts the subagent's ExportDomain in its target MBean server. The master agent then performs operations on the cascaded MBeans of the subagent. Press Enter to step through the example when the application pauses.

  4. You can interact with the example through the HTML adaptor of the master agent and subagent.

    This is demonstrated in How to Interact with a Cascade Hierarchy.

  5. If you are still receiving timer notifications on the master agent, press Enter once more to remove the listener, but leave both agent applications running.

How to Interact with a Cascade Hierarchy
  1. Open two browser windows side by side and load the following URLs:

    Subagent

    http://subAgent_hostname:8082/

    Master Agent

    http://MasterAgent_hostname:8084/

    In the subagent, you should see the timer MBean in the ExportDomain and a SimpleStandard MBean in the DefaultDomain.

    The master agent is recognizable by the cascading service MBean in com.sun.jdmk. Otherwise it has identical timer MBeans registered in the subagent/agent#/ExportDomain: this is the cascaded version of the timer in the subagent. The SimpleStandard MBean is not cascaded because our cascading service instance filters with the following object name pattern:

    ExportDomain:*

  2. Create four MBeans of the SimpleStandard class in following order:

    On the Master Agent:

    ExportDomain:name=SimpleStandard,number=1

    On the Subagent:

    ExportDomain:name=SimpleStandard,number=1

    ExportDomain:name=SimpleStandard,number=2

    ExportDomain:name=SimpleStandard,number=3

  3. Reload the agent view on the master agent.

    The cascaded MBeans for the last two agents have been created automatically. Look at the MBean view of either of these cascaded MBeans on the master agent. Their class name appears as SimpleStandard.

  4. In the master agent, set a new value for the State string attribute of all 3 of its SimpleStandard MBeans.

    When you look at the corresponding MBeans in the subagent, you see that all the MBeans were updated through the master agent.

  5. In the subagent, invoke the reset operation of all 3 of its SimpleStandard MBeans.

    When you inspect the MBeans in the master agent, the values for all were reset. Remember that the HTML adaptor must get the values of attributes for displaying them, so they were correctly retrieved from the cascaded MBeans that we reset.

  6. In the subagent, unregister MBeans number=1 and number=3, then update the agent view on the master agent.

    The cascaded MBean is automatically removed by the cascading service.

  7. Invoke the stop operation of the CascadingService MBean in the master agent.

    The last cascaded MBean for the timer is removed from the master agent. The two agents are no longer connected.

  8. If you have finished with the agents, press Enter in both of their terminal windows to exit the applications.