Monday, March 17, 2008

JMX, IOException, and NameNotFoundException

When using JMX connectors, it is important that the connector client URL and the connector server URL appropriately match. The only protocol required of remote JMX is RMI and so it is often RMI that is used in conjunction with remote JMX. There are several different types of exceptions that might occur depending on different JMX RMI URL problems, but the focus of this blog entry is on the NameNotFoundException.

The first code listing shown is a very simple JMX client.

JmxSpringClientMain.java

package client;

import java.io.IOException;
import java.net.MalformedURLException;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class JmxSpringClientMain
{
public static void main(String[] aArguments)
{
final String jmxRmiStr =
"service:jmx:rmi://localhost/jndi/rmi://localhost:1099/dustinjmxrmi";
try
{
final JMXServiceURL jmxUrl = new JMXServiceURL(jmxRmiStr);
final JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxUrl);
final MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();
System.out.println( "MBean Count: " + mbsc.getMBeanCount() );
System.out.println( "MBean Default Domain: " + mbsc.getDefaultDomain() );
}
catch (MalformedURLException badUrl)
{
System.err.println( "ERROR: Problem with JMXServiceURL based on "
+ jmxRmiStr + ": " + badUrl.getMessage() );
}
catch (IOException ioEx)
{
System.err.println( "ERROR: IOException trying to connect to JMX "
+ "Connector Server: " + ioEx.getMessage() );
}
}
}


The JMX Connector server that the above client would connect to could be set up programmatically, but I am going to use the Spring framework to do this for me. Here is an excerpt from the appropriate Spring configuration file.

spring-jmx-config.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-2.0.xsd">

<bean id="stateHandler"
class="dustin.StateHandler">
<property name="state" value="Colorado" />
<property name="capital" value="Denver" />
</bean>

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

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

<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/dustinjmxrmi"/>
</bean>

</beans>


If you compare the lines highlighted in the JMX Client code and in the Spring XML to be used to set up the server, you'll see that they match. If the last part of the JMXServiceURL is changed (from "dustinjmxrmi" to say "jmxrmi"), an IOException is thrown by the client with the following output:

Failed to retrieve RMIServer stub: javax.naming.NameNotFoundException: jmxrmi

Note that this is thrown as an IOException when the client code tries to access the JMX connector server. The NameNotFoundException mentioned in the IOException message extends NamingException.

The IOException/NameNotFoundException is a result of having matching host and port information between connector client and server, but having a mismatch on the name at the end of the JMX RMI URL. If the server is not up at all or is at a different host/port combination than specified by the JMX client, the exceptions underlying the resulting IOException in this case are ServiceUnavailableException and two varieties of ConnectException. The exact message is:

Failed to retrieve RMIServer stub: javax.naming.ServiceUnavailableException [Root exception is java.rmi.ConnectException: Connection refused to host: localhost; nested exception is: java.net.ConnectException: Connection refused: connect]

Like the NameNotFoundException, ServiceUnavailableException also extends NamingException.

For additional details on constructing a JMX RMI URL with JMXServiceURL, see Monitor Your Applications with JConsole - Part 3, JMX Accelerated HowTo (see section titled "RMI and JMXMP URLs"), the Sun JMX forum entry JMXServiceUrls, and DongWoo Lee's JMX RMI Proxy/NAT Connection image.

No comments: