Tuesday, February 12, 2008

Spring, JMX, RMI, and depends-on

In this blog entry, I'll demonstrate the importance of using the depends-on attribute when using Spring with JMX and a Spring-provided RMI Connector.

The next code snippet is an example of a basic Spring configuration file. Most of it is unimportant for this example, but the highlighted section shows what is really important for this blog entry.


<?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-2.0.xsd">

<bean id="jmxService"
class="marx.SimpleBean">
<description>A JMX Service</description>
<property name="action" value="Spring Action">
<description>Spring Action is described here.</description>
</property>
<property name="behavior" value="Spring Behavior" />
</bean>

<bean class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="dustin:type=marx"
value-ref="jmxService" />
</map>
</property>
<property name="assembler" ref="assembler" />
</bean>

<bean id="assembler"
class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
<property name="managedInterfaces">
<list>
<value>marx.SimpleBeanIf</value>
</list>
</property>
</bean>

<bean id="serverConnector"
class="org.springframework.jmx.support.ConnectorServerFactoryBean">
<property name="objectName" value="connector:name=rmi"/>
<property name="serviceUrl"
value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/>
</bean>

<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
<property name="port" value="1099"/>
</bean>

</beans>


I am not showing the actual bean class exposed here or the interface it implements because these details are not important for the purposes of this blog entry. When the full application is run with the XML as configured above, the following stack trace is encountered.



The key text of this exception stack trace is:

"Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serverConnector' defined in file [C:\NetBeansProjects\SpringJmxExample\SpringConfiguration.xml]: Invocation of init method failed; nested exception is java.io.IOException: Cannot bind to URL [rmi://localhost:1099/jmxrmi]: javax.naming.ServiceUnavailableException [Root exception is java.rmi.ConnectException: Connection refused to host: localhost; nested exception is: java.net.ConnectException: Connection refused: connect]."


There are a couple ways to make the error go away. One way is to remove the highlighted XML above and specify the RMI registry information on the command line using the following three flags:

  • -Dcom.sun.management.jmxremote.port=1099

  • -Dcom.sun.management.jmxremote.authenticate=false

  • -Dcom.sun.management.jmxremote.ssl=false



When the Spring-based application is run with the above three system parameters, the application works properly. However, this requires specifying the three parameters above on the command-line.

To keep the RMI registry information in the Spring XML configuration, the RMI registry entry either needs to be placed in the XML configuration file above the ConnectorServerFactoryBean entry or the serverConnector (ConnectorServerFactoryBean) element needs to reference the RMI registry. The second option, having the serverConnector reference the RMI registry is accomplished using the depends-on attribute. The modified code listing below shows the rearrangement of the RMI Registry entry to precede the serverConnector and it also shows use of the depends-on attribute. Either of these changes will make the example work without any special command-line system parameters, but I like to do both.


<?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-2.0.xsd">

<bean id="jmxService"
class="marx.SimpleBean">
<description>A JMX Service</description>
<property name="action" value="Spring Action">
<description>Spring Action is described here.</description>
</property>
<property name="behavior" value="Spring Behavior" />
</bean>

<bean class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="dustin:type=marx"
value-ref="jmxService" />
</map>
</property>
<property name="assembler" ref="assembler" />
</bean>

<bean id="assembler"
class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
<property name="managedInterfaces">
<list>
<value>marx.SimpleBeanIf</value>
</list>
</property>
</bean>
<!-- Placed RMI registry before serverConnector. -->
<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
<property name="port" value="1099"/>
</bean>

<bean id="serverConnector"
class="org.springframework.jmx.support.ConnectorServerFactoryBean"
depends-on="registry">
<property name="objectName" value="connector:name=rmi"/>
<property name="serviceUrl"
value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/>
</bean>

</beans>


As stated above, either action (placing the RMI registry in the Spring XML configuration before the serverConnector or using the depends-on attribute) will make the example work. The advantage of the depends-on attribute is that the XML file can be changed more safely without order of elements causing trouble.

Neither the use of the depends-on attribute or the ordering of the serverConnector (ConnectorServerFactoryBean) and registry (RmiRegistryFactoryBean) are as well advertised as one might expect, but information on these approaches are available in several locations.

One source of information on using depends-on to address this issue is the Spring forum message Spring, JMX, and RMI. Another source describing this is the Javadoc-generated API documentation for the RmiRegistryFactoryBean class. This class description includes this paragraph: "Also useful to enforce creation of a local RMI registry at a given port, for example for a JMX connector. If used in conjunction with ConnectorServerFactoryBean, it is recommended to mark the connector definition (ConnectorServerFactoryBean) as 'depends-on' the registry definition (RmiRegistryFactoryBean), to guarantee starting up the registry first."

The Spring 2.5 Reference does not address the depends-on attribute or importance of ordering in its JMX section, but it does describe that depends-on is occasionally necessary.

8 comments:

Paula said...

Justin, nice post - you are a saviour. You certainly saved me a lot of time and aggravation. Thanks!

Dustin said...

Paula,

Thanks for letting me know that this was helpful. It makes it more rewarding to post these things when I know that it has helped someone. I am glad it saved you time and aggravation because it certainly cost me both of these until I realized what I was doing wrong.

Zeitblende said...

Great post. Thank you for saving my day. The depends-on did the trick.

Greetings

Holger

Dustin said...

Holger,

I am glad this was helpful. Thanks for taking the time to let me know.

Dustin

jhominal said...

I don't have much to say but your post just helped me out of that problem. Thank you very much.

However, I would like to know - how did you find out that solution? Or, more generally, what are your favored ways of finding solutions to that kind of problems?

Dustin said...

jhominal,

Thanks for letting my know this post was helpful. In general, how I approach a particular problem depends on the problem itself. In this case, it took me a little longer than I normally like to spend on a problem. I thought that it looked like something often seen when Spring configuration is not quite right because of a typo, case problem, or other example where names do not exactly match. I verified everything was the same and finally, in a somewhat desperate move similar to what I've had to do in the past with ordering of C++ libraries, tried re-arranging the order of the elements. Another thing I'll often do is Google for the error message I am seeing.

jwtodd said...

Anyone figure out how to do this w/ spring annotations (roo)? Simply instantiating RmiRegistryFactoryBean before ConnectorServerFactoryBean isn't doing the trick for me :|

Metari Gangadhar said...

Hi,

I have developed the simple application and trying to expose them as remotely. have used the same code what u have used but when my application start up its giving some error, could able to find the solution can you please help me on this.

Error Details:-
Unable to register MBean [javax.management.remote.rmi.RMIConnectorServer@1375b87e] with key 'serverConnector'; nested exception is javax.management.MalformedObjectNameException: Key properties cannot be empty
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1412)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:574)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:83)
at com.PhilipsClient.main(PhilipsClient.java:19)
Caused by: org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [javax.management.remote.rmi.RMIConnectorServer@1375b87e] with key 'serverConnector'; nested exception is javax.management.MalformedObjectNameException: Key properties cannot be empty
at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:602)
at org.springframework.jmx.export.MBeanExporter.registerBeans(MBeanExporter.java:527)
at org.springframework.jmx.export.MBeanExporter.afterPropertiesSet(MBeanExporter.java:413)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1469)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1409)
... 12 more
Caused by: javax.management.MalformedObjectNameException: Key properties cannot be empty
at javax.management.ObjectName.construct(ObjectName.java:483)
at javax.management.ObjectName.(ObjectName.java:1382)
at javax.management.ObjectName.getInstance(ObjectName.java:1273)
at org.springframework.jmx.support.ObjectNameManager.getInstance(ObjectNameManager.java:62)
at org.springframework.jmx.export.naming.KeyNamingStrategy.getObjectName(KeyNamingStrategy.java:140)
at org.springframework.jmx.export.MBeanExporter.getObjectName(MBeanExporter.java:728)
at org.springframework.jmx.export.MBeanExporter.registerBeanInstance(MBeanExporter.java:631)
at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:592)
... 16 more



and my configuration is


















Thanks,
Gangadhar