Search code examples
jmxmbeans

Behavior MBeanServerForwarder


I have asked a question here but I did not get an answer. Howerver, I continue my search and I found something which could please my need : "MBeanServerForwarder". I read the official JavaDoc but it is still not clear for me.

So, does MBeanServerForwarder work as a proxy for a MBeanServer? ie: Can I use it to intercept MBeans registry, make modification in the ObjectName and forward it to the MBeanServer?

Thanks in advance.


Solution

  • Yes, but it's not really necessary. You only need to implement the MBeanServer interface and override the registerMBean method (and perhaps the unregisterMBean method).

    Using the real MBeanServer as a delegate, here's what your implementation might look like:

        public class AltObjectNameMBeanServer implements MBeanServer {
            protected final MBeanServer innerServer;
            protected final ObjectName filter;
    
            public AltObjectNameMBeanServer(MBeanServer innerServer, ObjectName filter) {
                this.innerServer = innerServer;
                this.filter = filter;
            }
    
            public ObjectInstance registerMBean(Object object, ObjectName name) throws InstanceAlreadyExistsException,
                    MBeanRegistrationException, NotCompliantMBeanException {
                if(filter.apply(name)) {
                    name = reformat(name);          
                }
                return innerServer.registerMBean(object, name);
            }
    
            public static ObjectName reformat(ObjectName on) 
                try {
                    int id = on.toString().hashCode();
                    return new ObjectName(new StringBuilder(on.toString()).append(",serial=").append(id).toString());
    
                } catch (Exception e) {
                    throw new RuntimeException("Failed to reformat [" + on + "]", e);
                }
            }
    
         // ======== Put direct delegates for all other methods =======
    
    }
    

    Sample usage:

            MBeanServer server = ManagementFactory.getPlatformMBeanServer();
            AltObjectNameMBeanServer rr = new AltObjectNameMBeanServer(server, new ObjectName("*:*"));          
            Class clazz = Class.forName("sun.management.HotspotInternal");
            HotspotInternalMBean obj = (HotspotInternal)clazz.newInstance();
            ObjectInstance oi = rr.registerMBean(new StandardMBean(obj, HotspotInternalMBean.class), new javax.management.ObjectName("sun.management:type=HotspotInternal"));
            System.out.println("ObjectName:" + oi.getObjectName());
    

    The output is:

    ObjectName:sun.management:type=HotspotInternal,serial=-441253090

    With a bit of reflection, you could set your new MBeanServer impl. in the platformMBeanServer field of the java.lang.management.ManagementFactory and you will have permanently overriden the JVM agent's MBean registration.

    ========== Update ============

    This code snippet demonstrates how you might hack the platform MBeanServer to supply an alternate (or wrapped) MBeanServer (using the rr instance of AltObjectNameMBeanServer from above:

    Field serverField = ManagementFactory.class.getDeclaredField("platformMBeanServer");
    serverField.setAccessible(true);
    serverField.set(null, rr);
    System.out.println("Equal:" + (rr==ManagementFactory.getPlatformMBeanServer()));
    

    ========== Update ============

    Here's a simple example of what I think you are looking for. See this gist.

    If you run the example with these options:

    -Djavax.management.builder.initial=org.helios.jmx.HeliosMBeanServerBuilder
    -Dhelios.jmx.renamer.filter=java.util.logging:*
    

    It will override the system's default MBeanServerBuilder and intercept all MBean registrations with ObjectNames matching java.util.logging:*.

    When the main runs, it will print all the platform MBeanServer MBean ObjectNames and the output will look like this:

    MBeanServer Interceptor Test
    java.lang:type=MemoryPool,name=PS Eden Space
    java.lang:type=Memory
    java.lang:type=MemoryPool,name=PS Survivor Space
    java.lang:type=GarbageCollector,name=PS MarkSweep
    java.lang:type=MemoryPool,name=Code Cache
    java.lang:type=Runtime
    java.lang:type=ClassLoading
    java.lang:type=Threading
    java.lang:type=Compilation
    com.sun.management:type=HotSpotDiagnostic
    java.lang:type=MemoryPool,name=PS Perm Gen
    java.util.logging:type=Logging,serial=-132046985
    java.lang:type=OperatingSystem
    java.lang:type=GarbageCollector,name=PS Scavenge
    java.lang:type=MemoryPool,name=PS Old Gen
    java.lang:type=MemoryManager,name=CodeCacheManager
    JMImplementation:type=MBeanServerDelegate
    

    Note the renaming of java.util.logging:type=Logging,serial=-132046985

    Alternatively, you can create your own instance of the HeliosMBeanServer builder, define the domain name, the filter and the rename strategy and create your own MBeanServer rather than using the platform MBeanServer.