JMX MBean registration using Spring on a standalone JVM
Solution 1
In addition to defining an MBeanServerFactory bean (as Nicholas noted in their answer) using ...
<bean class="org.springframework.jmx.support.MBeanServerFactoryBean">
<property name="locateExistingServerIfPossible" value="true" />
</bean>
... you need to tell the MBeanExporter what to manage:
If a bean implements one of the JMX management interfaces, MBeanExporter can simply register the MBean with the server through its autodetection process.
If a bean does not implement one of the JMX management interfaces, MBeanExporter will create the management information using the supplied MBeanInfoAssembler.
Assuming your abc.def.ghi.DH
class does not implement any JMX interface, try defining your MBeanExporter
as:
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="assembler">
<bean
class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler"
>
<property name="managedMethods">
<list>
<value>getNum</value>
</list>
</property>
</bean>
</property>
<property name="beans">
<map>
<entry key="bean:name=dH1" value-ref="dH"/>
</map>
</property>
</bean>
Looking at the OpenJDK 7, update 2, build 21 DefaultMBeanServerInterceptor.java
source, line 898 creates a DynamicMBean
for regular objects:
DynamicMBean mbean = Introspector.makeDynamicMBean(object);
I haven't debugged it, but I bet mbeanServer.registerMBean(dh, new ObjectName("bean:name=dH1"))
eventually calls DefaultMBeanServerInterceptor.registerObject()
, which creates a DynamicMBean
for you and properly registers your standard JavaBean
properties' setters and getters.
Here are some test files that work using Spring Framework 3.0.5 and Oracle HotSpot Java 1.6.0_24. After setting your CLASSPATH
environment variable, just run javac *.java
and java Main
and use VisualVM (or similar application) to connect to the running java application to see the registered MBeans.
ac.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-lazy-init="true"
>
<bean id="test" class="Test" />
<bean class="org.springframework.jmx.support.MBeanServerFactoryBean">
<property name="locateExistingServerIfPossible" value="true" />
</bean>
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="assembler">
<bean
class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler"
>
<property name="managedMethods">
<list>
<value>getVal</value>
<value>setVal</value>
</list>
</property>
</bean>
</property>
<property name="beans">
<map>
<entry key="bean:name=Test" value-ref="test"/>
</map>
</property>
</bean>
</beans>
Test.java:
public class Test {
private String val = "";
public String getVal() {
return val;
}
public void setVal(String v) {
val = v;
}
}
Main.java:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(final String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("ac.xml");
try {
Thread.sleep(1000 * 60 * 5);
} catch (final Throwable t) {}
}
}
Solution 2
The issue is with the MBeanServerFactoryBean.
From the javadoc:
By default, MBeanServerFactoryBean will always create a new MBeanServer even if one is already running. To have the MBeanServerFactoryBean attempt to locate a running MBeanServer first, set the value of the "locateExistingServerIfPossible" property to "true".
Try this config:
<bean class="org.springframework.jmx.support.MBeanServerFactoryBean">
<property name="locateExistingServerIfPossible" value="true" />
</bean>
=================================================
Try specifying the MBeanServer in the exporter bean:
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="beans">
<map>
<entry key="bean:name=dH1" value-ref="dH" />
</map>
</property>
<property name="server" ref="MBeanServer" />
</bean>
<bean id="MBeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
<property name="locateExistingServerIfPossible" value="true" />
</bean>
========================================================================
Ok, let's take the brute force approach and acquire the platform MBeanServer directly:
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="beans">
<map>
<entry key="bean:name=dH1" value-ref="dH" />
</map>
</property>
<property name="server">
<bean id="MBeanServer" class="java.lang.management.ManagementFactory" factory-method="getPlatformMBeanServer"/>
</property>
</bean>
Related videos on Youtube
Comments
-
Monis Iqbal almost 2 years
Following various example configurations from Spring documentation as well as some forums on the Internet, my application context file looks like:
<beans> <bean id="dH" class="abc.def.ghi.DH"> <constructor-arg> <value>0</value> </constructor-arg> <property name="num" value="100"/> </bean> <bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false"> <property name="beans"> <map> <entry key="bean:name=dH1" value-ref="dH"/> </map> </property> </bean> <bean class="org.springframework.jmx.support.MBeanServerFactoryBean"/> </beans>
I'm running this without any container and on plain JVM. I'm able to connect to my process via JConsole but the MBean doesn't show up. However registering the bean programmatically exposes it successfully.
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer(); DH dh = new DH(0); mbeanServer.registerMBean(dh, new ObjectName("bean:name=dH1"));
I've tried playing with the Spring configuration without success. I think the bean is not registering to the already running MBean server that was accessible from ManagementFactory.getPlatformMBeanServer(). Any ideas on the issue?
-
Monis Iqbal about 12 yearsThanks for the other suggestions @Nicholas. Though trying them both didn't register the MBeans as well.
-
Monis Iqbal about 12 yearsThanks for digging this out @Dan. When Spring context is loaded or even the dh is created using the context the Introspector line is not even executed. However registering the bean manually does call the same line. I don't think Spring is trying to register the bean with these configurations. Something must be missing...
-
Go Dan about 12 yearsThis works for me using Spring 3.0.5 and a simple JavaBean with one property getter/setter; I can connect to my simple test app with VisualVM and see the test MBean that was registered using the
MBeanExporter
bean definition I provided. Try breaking your scenario down to a simple form, get that to work, then build it back up to what you currently have and see where your issue lies. -
Monis Iqbal about 12 yearsUsing Spring 3.1.0 I tried with your provided XML with and without -Dcom.sun.management.jmxremote as program option, still no luck. I'm loading the context in the following manner: BeanFactory factory = new XmlBeanFactory(new ClassPathResource("/application-context.xml")); and then loading the intended MBean as: factory.getBean(DH.class); I know this sounds naive but is there a way we can match our complete XMLs and simple program structure?
-
Monis Iqbal about 12 yearsThanks a bunch @Dan. It was the way the beans were getting initialized :|. I was using BeanFactory whereas using the ApplicationContext did the trick.