JMX MBean registration using Spring on a standalone JVM - java

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?

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) {}
}
}

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

spring boot autowired object is getting set to null in ignite persistence cachestore factory class

I am using Apache Ignite 2.3 with cassandra 2.1.9 as my persistence layer.
I am using cacheStoreFactory class which saves and gets data from the db.
I am auto-wiring some dependencies in this class but it is coming as null.
here is my sample ignite confifuration file:
<?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
">
<description>Main Spring file for ignite configuration.</description>
<bean id="cacheIgniteBean" class="org.apache.ignite.IgniteSpringBean">
<property name="configuration">
<bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
<property name="dataStorageConfiguration">
<bean class="org.apache.ignite.configuration.DataStorageConfiguration">
<property name="dataRegionConfigurations">
<list>
<!--
Defining a data region that will consume up to 2 GB of RAM.
-->
<bean class="org.apache.ignite.configuration.DataRegionConfiguration">
<!-- Custom region name. -->
<property name="name" value="2GB_Region"/>
<!-- 500 MB initial size (RAM). -->
<property name="initialSize" value="#{500L * 1024 * 1024}"/>
<!-- 2 GB maximum size (RAM). -->
<property name="maxSize" value="#{2L * 1024 * 1024 * 1024}"/>
<!-- Enabling RANDOM_LRU eviction for this region. -->
<property name="pageEvictionMode" value="RANDOM_LRU"/>
</bean>
</list>
</property>
</bean>
</property>
<property name="cacheConfiguration">
<list>
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<property name="name" value="item"/>
<property name="cacheMode" value="PARTITIONED"/>
<property name="atomicityMode" value="ATOMIC"/>
<property name="backups" value="0"/>
<property name="cacheStoreFactory">
<bean class="javax.cache.configuration.FactoryBuilder" factory-method="factoryOf">
<constructor-arg value="com.tgt.gom.cacheserver.store.ItemCacheStore"/>
</bean>
</property>
<property name="readThrough" value="${ignite.config.cache.item.readThrough}"/>
<property name="writeThrough" value="${ignite.config.cache.item.writeThrough}"/>
<property name="writeBehindEnabled" value="${ignite.config.cache.item.writeBehindEnabled}"/>
<property name="writeBehindFlushSize"
value="${ignite.config.cache.item.writeBehindFlushSize}"/>
<property name="writeBehindFlushFrequency"
value="${ignite.config.cache.item.writeBehindFlushFrequency}"/>
<property name="writeBehindFlushThreadCount"
value="${ignite.config.cache.item.writeBehindFlushThreadCount}"/>
<property name="writeBehindBatchSize"
value="${ignite.config.cache.item.writeBehindBatchSize}"/>
</bean>
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<property name="name" value="location"/>
<property name="cacheMode" value="PARTITIONED"/>
<property name="atomicityMode" value="ATOMIC"/>
<property name="backups" value="0"/>
<property name="cacheStoreFactory">
<bean class="javax.cache.configuration.FactoryBuilder" factory-method="factoryOf">
<constructor-arg value="com.tgt.gom.cacheserver.store.LocationCacheStore"/>
</bean>
</property>
<property name="readThrough" value="${ignite.config.cache.item.readThrough}"/>
<property name="writeThrough" value="${ignite.config.cache.item.writeThrough}"/>
<property name="writeBehindEnabled" value="${ignite.config.cache.item.writeBehindEnabled}"/>
<property name="writeBehindFlushSize"
value="${ignite.config.cache.item.writeBehindFlushSize}"/>
<property name="writeBehindFlushFrequency"
value="${ignite.config.cache.item.writeBehindFlushFrequency}"/>
<property name="writeBehindFlushThreadCount"
value="${ignite.config.cache.item.writeBehindFlushThreadCount}"/>
<property name="writeBehindBatchSize"
value="${ignite.config.cache.item.writeBehindBatchSize}"/>
</bean>
</list>
</property>
<!--<property name="includeEventTypes">
<util:constant static-field="org.apache.ignite.events.EventType.EVTS_TASK_EXECUTION"/>
</property>-->
<property name="failureDetectionTimeout" value="5000"/>
<property name="discoverySpi">
<bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
<property name="ipFinder">
<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
<!-- <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder"> -->
<property name="addresses">
<list>
<value>127.0.0.1:47500..47509</value>
</list>
</property>
</bean>
</property>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
Here is my ItemCacheStore class code:
#Slf4j
#Service
public class ItemCacheStore extends CacheStoreAdapter<String, ItemV1DTO> implements Serializable {
private static final long serialVersionUID = 1L;
#Autowired
private ItemRepository itemRepository;
#Autowired
private ItemCacheStoreAsync itemCacheStoreAsync;
private static final String LOG_OP_INFO = "Item_Cache_Store";
#Override
public ItemV1DTO load(String item_id) throws CacheLoaderException {
ItemV1DTO itemV1DTO = null;
System.out.println("in item cache store ");
try {
ItemEntity itemEntity = itemRepository.findOne(item_id);
if (itemEntity != null) {
itemV1DTO = mapToItemDTO(itemEntity);
}
} catch (Exception e) {
throw new CacheLoaderException("failed to load item data from cassandra" + e.getMessage());
}
return itemV1DTO;
}
}
In ItemCacheStore class when the load method is called, the itemRepository field is null. However, when I autowire the same ItemRepository bean in another controller class, it works fine.
One more thing I noticed is that if I put one method with #PostConstruct annotation in the ItemCacheStore class then at that time I can see that dependency of ItemRepository got injected but when load method is called then it is again null.
The issue is the following configuration:
<property name="cacheStoreFactory">
<bean class="javax.cache.configuration.FactoryBuilder" factory-method="factoryOf">
<constructor-arg value="com.tgt.gom.cacheserver.store.ItemCacheStore"/>
</bean>
</property>
You're creating a Spring bean of type FactoryBuilder and you pass the classname ItemCacheStore. What happens behind the screens is that the FactoryBuilder will create a new instance of ItemCacheStore. This new instance won't be managed by Spring, so all fields will be null.
So basically you'll end up with two instances of ItemCacheStore:
One created by the Spring container thanks to the #Service annotation. All autowired fields will work.
Another one create by the FactoryBuilder. It won't be managed by Spring and all autowired fields will be null.
To fix this, there are a few possibilities:
Use a different factory or write your own that will use a Spring managed bean rather than creating a new one. According to the documentation, there is already a CassandraCacheStoreFactory.
Use an AutowireHelper as mentioned in this answer, or use Springs SpringBeanAutowiringSupport to inject beans in a non-Spring managed bean.
Related: Why is my Spring #Autowired field null?

Spring MVC 3.2.8: Create a new FileSystemXmlApplicationContext and loading the definitions from the given XML files

I have an Spring MVC 3.2.8 app, and I want to run StandAlone process to generate a PDF. I want to initialize the container and manage beans from an stand-alone app.
I have this piece of code:
public class CreatePDF {
private static final Logger LOGGER = Logger.getLogger (ImportEcolabelToolboxToECAT.class);
public static void main(String[] args) {
String[] configLocations = new String[] {
"C:/Development/Workspaces/EclipseWS/devices/deviceWeb/src/main/resources/com/nicinc/dao/dataAccessContext.xml",
"C:/Development/Workspaces/EclipseWS/devices/deviceWeb/src/main/webapp/WEB-INF/dao/databaseMessageSource.xml",
"C:/Development/Workspaces/EclipseWS/devices/deviceWeb/src/main/resources/com/nicinc/services/impl/servicesContext.xml",
"C:/Development/Workspaces/EclipseWS/devices/deviceWeb/src/main/webapp/WEB-INF/applicationContext.xml",
"C:/Development/Workspaces/EclipseWS/devices/deviceWeb/src/main/resources/com/nicinc/controller/propertyeditors/propertyeditorsContext.xml"};
FileSystemXmlApplicationContext ctx =
new FileSystemXmlApplicationContext(configLocations, true);
}
}
But I have this error when running the app.
Error creating bean with name 'dataSource': Invocation of init method failed; nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
and here the definition from the file dataAccessContext.xml :
<!-- The PropertyPlaceholderConfigurer replaces placeholders in Spring bean definitions with the values from the chosen properties files. -->
<!-- There is an example use in the datasource definition below. Look for the $\{jdbc.*} values. -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:com/nicinc/dao/jdbc-test.properties</value>
<value>classpath:com/nicinc/dao/dbMessageSource.properties</value>
</list>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="${hibernate.dialect}"/>
<property name="generateDdl" value="false"/>
<property name="showSql" value="false" />
</bean>
</property>
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
The javax.naming package comprises the JNDI Api. Since it is just an API its implementation you will have to provide. Generally the implementation is provided by App server. As per the error you are Missing the JNDI implementation.
Possible solution:
If you dont have any javaee related requirement then you should directly use DriverManagerDataSource.
You need to provide your own implementation.Below link might help.
using application data source locally.

org.springframework.jdbc.cannotgetjdbcconnectionexception Rmi class loader disabled

Rather new to Java coming from a c# background.
What I am trying to achieve is just to expose a method to jConsole via jmx and rim.
When I run my service and open jConsole I can see the method there and all looks good, now the problem comes in when i try to run this method via the console. The error I get is "Problem invoking helloWorld : java.rmi.UnmarshalException: Error unmarshaling return; nested exception is: java.lang.ClassNotFoundException: org.springframework.jdbc.CannotGetJdbcConnectionException (no security manager: RMI class loader disabled)".
The method im trying to expose is
#ManagedOperation
public int helloWorld() throws Exception {
return jdbcTemplate.queryForInt(sql);
}
Here is my applicationContext file
<!-- this bean must not be lazily initialized if the exporting is to happen -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="assembler" ref="assembler" />
<property name="namingStrategy" ref="namingStrategy" />
<property name="autodetect" value="true" />
</bean>
<bean id="jmxAttributeSource" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />
<!-- will create management interface using annotation metadata -->
<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="jmxAttributeSource" />
</bean>
<!-- will pick up the ObjectName from the annotation -->
<bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
<property name="attributeSource" ref="jmxAttributeSource" />
</bean>
<context:component-scan base-package="com.bulks" />
<!-- enable annotations like #Autowired, which you also most likely need -->
<context:annotation-config/>
<bean class="com.bulksms.resources.HelloWorldResource"/>
<!-- setup basic datasource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/apu"></property>
<property name="username" value="root"></property>
<property name="password" value="password"></property>
</bean>
<!-- jdbcTemplate bean -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
What am i missing so my method can be executed from the console?
-----SOLUTION------
So after hacking away at this for long i tried to put the sql part in its own method and then just call the method in the helloWorld method.... Low and behold!!!! SUCCESS!!!
public int helloWorld() throws Exception {
setValueFromQuery();
return value;
}
private void setValueFromQuery() {
this.value = jdbcTemplate.queryForInt(sql);
}
You exception is a nested exception, so its happened on on your application,
java.lang.ClassNotFoundException: org.springframework.jdbc.CannotGetJdbcConnectionException
So its say that there is a missing class, which could be the jdbc, make sure that you have it in your class path.
so if you have it, the check the connection criteria to your DB.

How to make embedded Jetty use the AppContext it's defined in as parent context for servlets

I have a standalone java app that now runs an embedded Jetty server to expose a RESTful API for HTTP. It does make heavy use of Spring beans for everything from Hibernate to Jetty. I have Jetty configured with a DispatcherServlet ( the thought being that adding a non-REST API in the future will be as simple as making the new Controller and mapping it correctly for the dispatcher).
My app has a class with a main method that creates a ClassPathXmlApplicationContext from my appContext.xml to start everything up.
ApplicationContext ac= new ClassPathXmlApplicationContext(new String[] { "appContext.xml" });
I don't know how to make beans defined in the context config file for the DispatcherServlet have access to beans defined in the appContext.xml where jetty is defined. My Jetty definition looks like this:
<bean id="JettyServer" class="org.eclipse.jetty.server.Server" init-method="start" destroy-method="stop">
<constructor-arg>
<bean id="threadPool" class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<property name="minThreads" value="2"/>
<property name="maxThreads" value="10"/>
</bean>
</constructor-arg>
<property name="connectors">
<list>
<bean id="Connector" class="org.eclipse.jetty.server.ServerConnector">
<constructor-arg ref="JettyServer"/>
<property name="port" value="8090"/>
</bean>
</list>
</property>
<property name="handler">
<bean id="handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
<property name="handlers">
<list>
<bean class="org.eclipse.jetty.servlet.ServletContextHandler">
<property name="contextPath" value="/"/>
<property name="servletHandler">
<bean class="org.eclipse.jetty.servlet.ServletHandler">
<property name="servlets">
<list>
<bean class="org.eclipse.jetty.servlet.ServletHolder">
<property name="name" value="DefaultServlet"/>
<property name="servlet">
<bean class="org.springframework.web.servlet.DispatcherServlet"/>
</property>
<property name="initParameters">
<map>
<entry key="contextConfigLocation" value="classpath:./DefaultServlet.xml" />
</map>
</property>
</bean>
</list>
</property>
<property name="servletMappings">
<list>
<bean class="org.eclipse.jetty.servlet.ServletMapping">
<property name="pathSpecs">
<list><value>/</value></list>
</property>
<property name="servletName" value="DefaultServlet"/>
</bean>
</list>
</property>
</bean>
</property>
</bean>
<bean class="org.eclipse.jetty.server.handler.RequestLogHandler">
<property name="requestLog">
<bean class="org.eclipse.jetty.server.NCSARequestLog">
<constructor-arg value="/opt/impulse/logs/jetty-yyyy_mm_dd.log"/>
<property name="extended" value="false" />
</bean>
</property>
</bean>
</list>
</property>
</bean>
</property>
</bean>
Then in DefaultServlet.xml I try to defined a bean with a property references a bean defined in appContext.xml, which is what breaks.
<bean id="restApiController" class="com.mycompany.myapp.api.controllers.RESTfulController">
<property name="someBean" ref="someBean"/>
</bean>
You are bootstrapping Jetty with applicationContext.xml, which in turn sets up jetty with your servlet configuration. Inside it you are configuring your servlet with the contextConfigLocation parameter pointing to the servlet application context. It will still run as a webapp, even if you embed it. So you need to provide your servlet with the config to your other beans as well. I suggest that you extract the jetty setup into it's own file, and then the rest of your beans in a different file. You then supply the other context file in the contextConfigLocation.
Edit
If you really need to share the application context between jetty and your servlet, maybe you can use some of the information in this blog. It seems to be possible, but it looks like you have to wire up the parent/child relationship between the contexts manually.
For me, what worked is setting of ResourceConfig. With DispatcherServlet server was not even able to serve Rest call. So I used ServletContainer. Now Rest call worked but not able to access beans loaded in ApplicationContext. There ResourceConfig registration helped. Below was my configuration that I came up after long R & D. I had Jetty version 9.2.11.v20150529 and Spring 4.1.2.RELEASE
<bean class="org.eclipse.jetty.servlet.ServletHolder">
<property name="name" value="DefaultServlet"/>
<property name="servlet">
<bean id="servletContainer" class="org.glassfish.jersey.servlet.ServletContainer">
<constructor-arg>
<ref bean="config" />
</constructor-arg>
</bean>
</property>
</bean>
<bean id="config" class="org.glassfish.jersey.server.ResourceConfig" />
Basically I set ResourceConfig under ServletContainer. Then in application, I fetched all beans loaded in my applicationContext and register with this Resource config like below
ResourceConfig restConfig = (ResourceConfig)webContext.getBean("config");
String[] beans = context.getBeanDefinitionNames();
for(String bean : beans)
restConfig.registerInstances(context.getBean(bean));
Well, webContext here is WebAppContext which is required instead of ServletContaxtHandler. So instead of below lines as mentioned in question
<bean class="org.eclipse.jetty.servlet.ServletContextHandler">
<property name="contextPath" value="/"/>
I have
<!-- To work with Spring , we need WebAppContext instead of ServletContext -->
<!-- <bean id="servletContextHandler" class="org.eclipse.jetty.servlet.ServletContextHandler"> -->
<constructor-arg name="webApp" value="./target/classes/" />
<constructor-arg name="contextPath" value="/" />

Change #ManagedResource objectName dynamically

I am creating prototype beans programatically/dynamically. I want those beans after initiation to be in the jmx console. How I can distinguish between them? I am using anotations in order to add my beans to the jmx and I have
#ManagedResource(objectName="bean:name=MybBean")
I need to inject the objectName dynamically. Any idea how could I do it?
Here's my jmx configuration:
<context:mbean-export server="mbeanServer" />
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean" />
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"
lazy-init="false">
<property name="beans">
<map>
<entry key="Server:name=HttpAdaptor">
<bean class="mx4j.tools.adaptor.http.HttpAdaptor">
<property name="port" value="8000" />
<property name="host" value="0.0.0.0" />
<property name="processor">
<bean class="mx4j.tools.adaptor.http.XSLTProcessor" />
</property>
</bean>
</entry>
</map>
</property>
<property name="listeners">
<list>
<!--
-->
<bean class="com.fixgw.jmx.HttpAdaptorMgr">
<property name="mbeanServer" ref="mbeanServer" />
</bean>
</list>
</property>
</bean>
<bean id="sessionMDB" class="com.fixgw.mdb.SessionMDB"
scope="prototype" lazy-init="true">
<constructor-arg ref="0" />
<constructor-arg ref="0" />
</bean>
You can do this by just implementing org.springframework.jmx.export.naming.SelfNaming:
#Component("MyPrototypeScopedBeanName")
#ManagedResource
public class MyPrototypeScopedBeanName implements SelfNaming
{
#Override
public ObjectName getObjectName() throws MalformedObjectNameException {
return new ObjectName("com.foobar", "name", this.toString());
}
}
You can use a a JMX naming strategy to do this. At work we use an interface:
public interface RuntimeJmxNames {
/** this is the name= part of the object name */
public String getJmxName();
/** this sets the folders as 00=FirstFolder,01=Second */
public String[] getJmxPath();
}
I've posted the code to implement the RuntimeMetadataNamingStrategy naming strategy.
And then something like the following Spring beans:
<bean id="jmxAttributeSource"
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />
<bean id="jmxAssembler"
class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="jmxAttributeSource" />
</bean>
<bean id="jmxNamingStrategy" class="com.j256.jmx.RuntimeMetadataNamingStrategy">
<property name="attributeSource" ref="jmxAttributeSource" />
</bean>
<bean id="mbeanExporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="autodetect" value="true" />
<property name="assembler" ref="jmxAssembler" />
<property name="namingStrategy" ref="jmxNamingStrategy" />
<property name="ensureUniqueRuntimeObjectNames" value="false" />
<property name="excludedBeans" ref="excludedJmxBeans" />
</bean>
In your code you do something like:
#ManagedResource(objectName = "foo.com:name=replaced", description = "...")
public class Foo implements RuntimeJmxNames {
...
public String getJmxName() {
// here's where you can make the name be dynamic
return toString();
}
#Override
public String[] getJmxPath() {
return new String[] { "folder" };
}
}
Here's the Spring documentation on JMX naming although I'm not 100% sure it covers the custom naming stuff.
Also, my SimpleJMX package does a this as well. It uses a JmxSelfNaming interface which allows each instance of an object to define it's own bean-name to make them unique and works well with Spring.

Categories