Chris
Chris

Reputation: 240

Is holding a java JMX connection open a good idea

I have a support website that I would like to show some stats gathered from another Java app via JMX. We have noticed the support app sometimes cannot get the stats after the other app has been restarted. I guess this is because the support app has opening a JMX connection to the other app and keeps hold of it. Then every time you go to the page to display the JMX stats it tries to gather them using the connection and it fails.

My question is, is it better to have a single JMX connection and try and work out when we should reconnect it?

Or each time we load the page with JMX stats on it should we create a new JMX connection then close it once we have the values we need?

Upvotes: 1

Views: 1399

Answers (2)

Chris
Chris

Reputation: 240

We didn't end up using a heartbeat but after reading Girish's answer came up with the following

public class JmxMetricsRetriever {

private final JMXServiceURL jmxUrl;
private final Map<String, Object> env;

private MBeanServerConnection connection;

private JmxMetricsRetriever(JMXServiceURL jmxUrl, Map<String, Object> env) {
    this.jmxUrl = jmxUrl;
    this.env = env;
    reconnect();
}

public synchronized Object getAttributeValue(String jmxObjectName, String attributeName) {
    try {
        if (connection == null) {
            reconnect();
        }
        try {
            return getAttributeValuePrivate(jmxObjectName, attributeName);
        } catch (ConnectException exc) {
            //This is to reconnect after the Server has been restarted.
            reconnect();
            return getAttributeValuePrivate(jmxObjectName, attributeName);
        }
    } catch (MalformedObjectNameException |
            AttributeNotFoundException |
            MBeanException |
            ReflectionException |
            InstanceNotFoundException |
            IOException ex) {
        throw new RuntimeException(ex);
    }
}

private synchronized Object getAttributeValuePrivate(String jmxObjectName, String attributeName) throws MalformedObjectNameException, MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException {
    ObjectName replication = new ObjectName(jmxObjectName);

    return connection.getAttribute(replication, attributeName);
}

private synchronized void reconnect() {
    logger.info(String.format("Reconnecting to [%s] via JMX", jmxUrl.toString()));
    try {
        JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxUrl, env);
        this.connection = jmxConnector.getMBeanServerConnection();
        jmxConnector.connect();
    } catch (IOException e) {
        //Log something but don't throw an exception otherwise our app will fail to start.
    }
}

public static JmxMetricsRetriever build(String url, String port, String user, String password) {
    try {
        JMXServiceURL jmxUrl = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + url + ":" + port + "/jmxrmi");
        Map<String, Object> env = new HashMap<>();
        env.put(JMXConnector.CREDENTIALS, new String[]{user, password});


        return new JmxMetricsRetriever(jmxUrl, env);
    } catch (MalformedURLException ex) {
        throw new RuntimeException(ex);
    }
}

}

When we start our app we try and create a JMX connect an hold on to it. Every time we get a JMX attribute we check the connection has been created (might not of been if the server we are connecting to was not up when we started our service). Then try and retrieve our attribute. If it failed try and reconnect and get the attribute value. We could not find a better way to test of a JMX connect was still usable so had to catch the exception.

Upvotes: 0

Maas
Maas

Reputation: 1357

As per my knowledge,

JMX connections are RMI Connector objects and hence can be held in the client app. + use a heartbeat approach to reconnect.

This way we can avoid overhead of re-establishing RMI connections which are not light weight.

Refer: javax.management.remote.rmi.RMIConnector

Upvotes: 1

Related Questions