Don't Do This! But Here Is a Way.

Recently I was asked how to do "registerMBean" in a remote JVM, from a remote JMX client. The MBeanServerConnection doesn't expose any registerMBean method, and for good reasons.

When you create and register an MBean in a local MBeanServer, the MBeanServer keeps a local reference to the MBean you registered. The MBean you registered, and the MBean you access through the MBeanServer interface are thus the same object.

However, if you try to register an MBean in a remote MBeanServer, the MBean will have to be serialized, and recreated as a new object in the remote JVM. There will thus be two object: one in the local JVM (client side) - and a copy of it in the remote JVM server side. These two object are completely unrelated, and a modification in one of them will not affect the other in any way: they are two different copies...

This is the first reason why MBeanServerConnection doesn't have any "registerMBean" method: it would make you think that you register an object x, while in fact you're registering a distinct serialized clone.

The second reason is that an MBean usually reflect the state of an application resource. In most cases, the MBean will have a pointer to that resource. Very often, that resource will not be serializable. In that case, cloning the MBean to send it to other side will be at best impossible and at worst undesirable.

There are times however where you do need to create an MBean in a remote JVM. But the proper way to that is not to use registerMBean, but one of the createMBean methods exposed by MBeanServer. With createMBean there are no confusion possible: you create a new MBean in a remote JVM, and you can give it all the information it needs to initialize in its constructor.
If you ever need to create an MBean in a remote JVM, createMBean is thus definitely the way to go.

However, if after reading all of this you still want to do "registerMBean" from remote, here is a very simple trick that you can use. It works only with Standard MBeans and MXBeans - and won't work with DynamicMBeans. But I'll say it another time: don't do this before you really know what you're doing and all its implications!

Please don't do THIS:

/\*
 \* DontDoThis.java
 \*
 \* Copyright 2008 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.
 \* 
 \* Created on Jul 25, 2007, 11:42:49 AM
 \* 
 \*/

package dontdothis;

import java.io.IOException;
import java.io.Serializable;
import java.lang.management.ManagementFactory;
import javax.management.JMException;
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

/\*\*
 \* This trick only works with standard MBean and MXBeans.
 \* @author dfuchs
 \*/
public class DontDoThis {

    // A dummy interface for my dummy MBean
    public static interface ThingMBean {
        // Get A foo for my thing
        public String getFoo();
        // Set A foo for my thing
        public void setFoo(String value);
    }

    // A dummy implementation for my dummy MBean
    public static class Thing implements ThingMBean, Serializable {
         // Attribute : Foo
        private String foo;

        public Thing(String foo) {this.foo = foo;}
        // Get A foo gor my thing
        public String getFoo() {return foo;}
        // Set A foo gor my thing
         public void setFoo(String value) {foo = value;}
    }

    // Create a server... we're simulating a remote server
    // here by starting an RMI JMXConnectorServer...
    public static JMXServiceURL createServer() 
            throws IOException {         
        final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        final JMXServiceURL jurl = new JMXServiceURL("rmi", null, 0);
        final JMXConnectorServer server =
                JMXConnectorServerFactory.newJMXConnectorServer(jurl, null, mbs);
        server.start();
        return server.getAddress();
    }
    
    // Here is the trick: instead of registering x directly, we wrap it
    // in an instance of javax.management.StandardMBean, and we use
    // createMBean to create the wrapper.
    //
    public static ObjectInstance registerMBean(MBeanServerConnection mbsc,
            ObjectName mbeanName, Object mbean, Class<?> mbeanInterface) 
            throws IOException, JMException {
        
        // create a Standard MBean,
        return mbsc.createMBean(StandardMBean.class.getName(), mbeanName,
                new Object[]{mbean, mbeanInterface}, 
                new String[]{Object.class.getName(),Class.class.getName()});
    }
    
    // Well: you've been warned: don't do this!
    public static void main(String[] args) throws Exception {
        
        final JMXServiceURL serverURL = createServer();

        // Instantiate the MBean we want to register
        final ThingMBean x = new Thing("I am a nice Thing!");
        final ObjectName xName = new ObjectName("x:x=x");
        
        // we're simulating a remote client by connecting to the server
        // through RMI
        final JMXConnector c = JMXConnectorFactory.connect(serverURL);
        final MBeanServerConnection mbsc = c.getMBeanServerConnection();
        
        // register MBean x in remote server.
        registerMBean(mbsc,xName,x,ThingMBean.class);

        // create a proxy to the registered MBean
        final ThingMBean x2 = JMX.newMBeanProxy(mbsc, xName, ThingMBean.class);

        // modify the original reference:
        x.setFoo("... but I have turned bad.");

        // x & the registered MBean are now 2 different objects!
        System.out.println(xName.toString() + " says: " + x2.getFoo());
        System.out.println("x says: " + x.getFoo());

        System.out.println("Now waiting for Enter: ");
        System.in.read();

    }
}
                            
Comments:

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