Federating MBeanServers with OpenDMK

Project OpenDMK includes a cascading API that makes it possible to federate MBeanServers. When several applications are running in the same JVM, this example shows how the OpenDMK cascading API can be used to keep applications MBeans isolated by using one MBeanServer per application instance, but still presenting a single point of access to remote clients by federating all MBeanServers in a single global MBeanServer.

Federation of MBeanServers is also discussed for JMX 2.0. Although it does share some concepts with the cascading feature of OpenDMK, it will also have some noticeable differences.

In OpenDMK, the cascading API lets you federate MBeanServers in a global MBeanServer by prefixing the domain name of federated MBean ObjectNames with a directory name prefix. This is somewhat analogous to a filesystem mount operation, where each MBeanServer would be seen as a filesystem directory (folder).

If you mount an MBeanServer A into the directory "serverA" of your global MBeanServer, then all MBeans belonging to MBeanServer A will be mounted in the global MBeanServer with an ObjectName whose domain begin with the string "serverA/".

So for instance, the MBeanServerDelegateMBean of MBeanServer A appears in the global MBeanServer as:

serverA/JMImplementation:type=MBeanServerDelegate

In JMX 2.0 we're planning to keep this concept - but we will be using "//" as separator instead of "/", so as not to conflict with existing ObjectNames. Moreover, MBeans whose names contain a "//" will no longer exist as MBeans in the MBeanServer but will need to be handled by a special object called a NamespaceHandler.
Another important difference in JMX 2.0 is that MBeans whose names contain a "//" will not be returned by MBeanServer.queryNames(null,null);.
The federation feature is still being discussed in JSR 255 expert group, and thus I do not want to launch in a detailed explanation of its concepts and APIs yet, but I wanted to give a warning here that although some of its concepts are quite close to what can be seen in the cascading feature of OpenDMK today, some of it will also be significantly different:
The JMX 2.0 federation feature will be based on the concept of ObjectName namespaces, which can have broader applications and address shortcomings not handled by the OpenDMK Project's API, such as scalability.

So do not be confused in thinking that OpenDMK cascading feature is the code base for what will be in JMX 2.0: it's not.

An example of federated MBeanServers with OpenDMK cascading

Some time ago I answered a question on the SDN JMX forum, from someone who wanted to run several applications in the same JVM, but keep the MBeans of each application isolated from each other. Here is how this can be achieved with OpenDMK cascading feature.
This example shows:

Example Source Tree

A Main class that creates applications whose MBeans are registered in an application-private MBeanServer, and then federates all these MBeanServers in a global MBeanServer - this example uses the platform MBeanServer: if you wanted to keep all applications really isolated and prevent them to access each other's MBeans by any means, and didn't want to set up a complex java policy file, you would probably create and use another MBeanServer for that, since the application code could also call ManagementFactory.getPlatformMBeanServer().

An ApplicationContoller MBean that we use for the sake of the example: since I had to code and set up two dummy applications that basically did the same thing (create an application MBeanServer and register their MBeans in it) I choose to factorize all this common code in a base ApplicationController MBean - of which each application provide its own subclass. You can view these ApplicationContollers as the main JMX entry point (MBean setup) for each application. Of course in the real world you do not need your applications to share any code: all you need is a reference to a private MBeanServer in which the application has registered its own MBeans.

Dummy application MBeans for each applications. Application A has Wombat MBeans, and Application B has Bandicoot MBeans.

Global MBean Tree

The Main class creates an instance of Application A and mounts its MBeanServer in a directory "a" in the platform MBeanServer. Then it creates two instances of Application B, and mounts them in directories "b1" and "b2" respectively.

The tree on the left shows how the platform MBeanServer now looks like. If we were using JMX 2.0, we would not be seeing the mounted MBeans at this level. We would see only special MBeans called NamespaceHandlers, on which we would have to click in order to enter their namespaces (in this case, the corresponding private application MBeanServer).

The popup on the right part of the image shows all the MBeans that the BandicootMBean bandicoot0 was able to find using queryNames(null,null) on its MBeanServer reference. This is because each application is using a private MBeanServer, and the global MBeanServer in which they all have been federated was not provided to them.

The code below shows how the Main class uses OpenDMK APIs in order to mount each application MBeanServer into the global MBeanServer:

    
/\*
 \* Main.java
 \*
 \* Created on May 9, 2007, 5:51 PM
 \*
 \* SCCS: %W%
 \*
 \* Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
 \*
 \* Redistribution and use in source and binary forms, with or without
 \* modification, are permitted provided that the following conditions
 \* are met:
 \*
 \*   - Redistributions of source code must retain the above copyright
 \*     notice, this list of conditions and the following disclaimer.
 \*
 \*   - Redistributions in binary form must reproduce the above copyright
 \*     notice, this list of conditions and the following disclaimer in the
 \*     documentation and/or other materials provided with the distribution.
 \*
 \*   - Neither the name of Sun Microsystems nor the names of its
 \*     contributors may be used to endorse or promote products derived
 \*     from this software without specific prior written permission.
 \*
 \* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 \* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 \* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 \* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 \* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 \* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 \* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 \* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 \* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 \* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 \* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 \*/

package example.opendmk.cascading;

import com.sun.jdmk.remote.cascading.CascadingAgent;
import com.sun.jdmk.remote.cascading.LocalMBeanServerConnectionFactory;
import com.sun.jdmk.remote.cascading.MBeanServerConnectionFactory;
import com.sun.jdmk.remote.cascading.proxy.ProxyCascadingAgent;
import example.application.ApplicationController;
import example.application.a.ApplicationA;
import example.application.b.ApplicationB;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.logging.Logger;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

/\*\*
 \* In this example we're going to:
 \* <ul><li>
 \*     Create an instance of Application A and two instances of Application B,
 \* </li><li>
 \*     Register each application instance in its own MBeanServer,
 \* </li><li>
 \*     Mount each application's MBeanServer in a global MBeanServer.
 \* </li></ul>
 \* <p>
 \* For the sake of simplicity, we're going to use the platform MBeanServer
 \* as global MBeanServer. This will make it possible to use the default
 \* JVM agent to expose the applications MBeans.
 \* <p>
 \* @author Sun Microsystems, 2007 - All rights reserved.
 \*/
public class Main {
    
    /\*\*
     \* Main logger for this class.
     \*/
    private static final Logger LOG =
            Logger.getLogger(Main.class.getName());
        
    /\*\*
     \* Creates an OpenDMK cascading agent to mount a controlled application
     \* MBeanServer inside a global target MBeanServer.
     \* @param target the global MBeanServer which is the target of the mount.
     \* @param name the directory name in which we will mount the controlled
     \*             application MBeanServer.
     \* @param appl the ApplicationController for the controlled application 
     \*        whose MBeanServer we want to mount.
     \*\*/
    private static CascadingAgent createCascadingAgentFor(MBeanServer target,
            String name, ApplicationController appl) {
        final MBeanServerConnectionFactory mbsfa = 
                new LocalMBeanServerConnectionFactory(appl.getMBeanServer(),
                "application "+name);
        return new ProxyCascadingAgent(mbsfa,null,null,name,
                target,"application "+name);
    }
    
    /\*\*
     \* Mounts a controlled application MBeanServer inside a global target 
     \* MBeanServer.
     \* @param target the global MBeanServer which is the target of the mount.
     \* @param name the directory name in which we will mount the controlled
     \*             application MBeanServer.
     \* @param appl the ApplicationController for the controlled application 
     \*        whose MBeanServer we want to mount.
     \* @return The OpenDMK CascadingAgent which controls the mount point.
     \*\*/
    public static CascadingAgent mount(MBeanServer target, String name, 
            ApplicationController appl) 
        throws JMException, IOException {
        appl.register();
        final CascadingAgent ag = createCascadingAgentFor(target,name,appl);
        target.registerMBean(ag,getCascadingAgentNameFor(name));
        ag.start();
        return ag;
    }
    
    /\*\*
     \* A name for the OpenDMK CascadingAgent that will control the mount point
     \* for the given directory.
     \* @name the directory name controlled by this cascading agent.
     \*\*/
    public static ObjectName getCascadingAgentNameFor(String name) 
        throws MalformedObjectNameException {
       return new ObjectName("opendmk.cascading.agents:type=CascadingAgent,name="+name);
    }
    
    /\*\*
     \* In this example we're going to:
     \* <ul><li>
     \*     Create an instance of Application A and two instances of Application B,
     \* </li><li>
     \*     Register each application instance in its own MBeanServer,
     \* </li><li>
     \*     Mount each application's MBeanServer in a global MBeanServer.
     \* </li></ul>
     \* <p>
     \* For the sake of simplicity, we're going to use the platform MBeanServer
     \* as global MBeanServer. This will make it possible to use the default
     \* JVM agent to expose the applications MBeans.
     \* <p>
     \* We will then be able to see \*all\* MBeans with JConsole, but you will
     \* notice that applications MBean can only see the MBeans registered
     \* in their own application instance MBeanServer (use JConsole to call
     \* WombatMBean.explore() or BandicootMBean.explore().
     \* @param args none.
     \*/
    public static void main(String[] args) throws Exception {
        final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        final ApplicationA a  = new ApplicationA("a");
        final ApplicationB b1 = new ApplicationB("b1");
        final ApplicationB b2 = new ApplicationB("b2");
        final CascadingAgent ca  = mount(server,"a",a);
        final CascadingAgent cb1 = mount(server,"b1",b1);
        final CascadingAgent cb2 = mount(server,"b2",b2);
        System.out.println("Now use JCondsole to connect to " +
                ManagementFactory.getRuntimeMXBean().getName());
        System.out.println("Strike <Enter> to exit: ");
        System.in.read();
    }
    
}

You can get a binary (or source) zip of the OpenDMK APIs from http://opendmk.dev.jav.net/ and you can download a zip containing the sources of the example discussed in this blog entry from here.

Cheers, and let me know if I've been too concise!
-- daniel

Comments:

Hi,
really interesting example.
I've to manage some mBeans in few mBeanServers. So I tried your example (I've only added a filter like mydomain:\*,myAttr=true in ProxyCascadingAgent creation).
two qustions:
- How is possible to have a dynamic mirror of my managed Mbeans?...e.g. I connect with mBean servers once, and I'd be notified when an mbean register/deregister;
- What happen when jmx connection shut down?

thanks
luke

Posted by lukebike on March 13, 2008 at 09:13 AM CET #

Hi Luke,

I don't understand your question, because it's already a dynamic mirror...

Best regards,

-- daniel

Posted by daniel on March 19, 2008 at 04:14 AM CET #

The only difference to blog example is that my subagent application run on a different jvm and I retry remote mbeanServer connection via rmi.

In this case it seems that when I add/remove an Mbean in subagent MbeanServer, my masterAgent isn't updated....

thanks

Posted by lukebike on March 20, 2008 at 02:23 AM CET #

Hi Luke,

This is strange, and is not an expected behaviour - the ProxyCascadingAgent registers for MBeanServerNotification with the subagent in order to add/remove proxies when MBeans are added/removed in the sub agent.

How did you create set up the cascading link between the master agent and the subagent?

regards,

-- daniel

Posted by daniel on March 21, 2008 at 03:27 AM CET #

I've implemented something like this:

MBeanServer target = MBeanServerFactory.createMBeanServer();

MBeanServerConnection remoteConn = ....//retry a connection with subAgtMbServer
CascadingAgent ag = createCascadingAgentFor(target,"myAppl",remoteConn);
target.registerMBean(ag,new ObjectName("opendmk.cascading.agents:type=CascadingAgent,name="+name));
ag.start();

private static CascadingAgent createCascadingAgentFor(MBeanServer target,
String name, MBeanServerConnection remoteMBserverC) {
try {
final MBeanServerConnectionFactory mbsfa = new LocalMBeanServerConnectionFactory(remoteMBserverC, "application " + name);

ObjectName filter = new ObjectName("mydomain:\*");//I'm only interested in manage mbeans in this domain

return new ProxyCascadingAgent(mbsfa, filter, null, name, target, "application " + name);
} catch (MalformedObjectNameException ex) {
return null;

} catch (NullPointerException ex) {
return null;

}
}

thank Daniel...and Happy Easter
luke

Posted by lukebike on March 21, 2008 at 06:08 AM CET #

Hi Luke,

Two remarks:

1) when federating remote MBeanServers I would recommend using the CasadingService MBean, rather than working directly with proxy cascading agents.

2) If you prefer to use the ProxyCascadingAgent directly for remote MBeanServers as you do know, you should use the BasicMBeanServerConnectionFactory instead of the LocalMBeanServerConnectionFactory.

The BasicMBeanServerConnectionFactory has additional logics to handle notification losts, connection closed notifications, etc...

Take care however that you must connect the JMXConnector before passing it to the BasicMBeanServerConnectionFactory.

Cheers,

-- daniel

Posted by daniel on March 21, 2008 at 07:41 AM CET #

Hi,
As I suggested you, I try to use CascadingService, as follow

JMXServiceURL agentURLs= ......
Hashtable h = new Hashtable();
h.put(Context.SECURITY_PRINCIPAL, username);
CascadingService service = new CascadingService();
service.mount(agentURLs[i],h,
new ObjectName("mySubAgtDomain:\*"), "subagents/agent"+(i+1));

// Store the mountPointID for further use.
mountPoints[i] = mountPointID;
System.out.println(mountPointID);

but again my masterAgent isn't updated after an Mbean has been added/removed in subagent MbeanServer

thanks

Posted by lukebike on March 25, 2008 at 09:30 AM CET #

Hi Luke,

This is strange indeed. If you have a test case for this I'll have a look at it. You can send it to me directly by email.
What kind of connector are you using (what protocol? service:jmx:rmi: ?)

regards,

-- daniel

Posted by daniel on March 25, 2008 at 09:50 AM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Daniel Fuchs blogs on Scene Builder, JMX, SNMP, Java, etc...

The views expressed on this blog are those of the author and do not necessarily reflect the views of Oracle.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today