JMX Proxy

JMX Proxy There are situations, where a deployment of an Jolokia agent on the target platform is not possible. This might be for political reasons or an already established JSR-160 export on the instrumented servers. In these environments, Jolokia can operate as a JMX Proxy. In this setup, the agent is deployed on a dedicated proxy Jakarta EE server (or other supported agent platform). The proxy bridges between Jolokia JSON request and responses to remote JSR-160 calls to the target server. The following diagrams gives an illustration of this setup.

Proxy Mode

A Jolokia proxy is universal and agnostic to the target server as it gets its information for the target via an incoming request (the same as for an HTTP proxy). Due to this required extended information, only Jolokia POST requests can be used for proxying since there is currently no way to encapsulate the target information within a GET URL. The base Jolokia URL for the request is that of the proxy server, whereas the target parameters are included in the request.

JMX Remote primer

The standard (but often forgotten) remoting technology for Java applications is RMI.

In order to access a javax.management.MBeanServer available withing a remote JVM, we should actually access a remote view of this interface. It is possible using Remote Monitoring and Management.

The connection is established between two entities specified in "Chapter 13, Connectors" of the unified JMX specification (combined JSR-3 and JSR-160):

  • connector server - it is attached to target javax.management.MBeanServer within the same JVM and listens for incoming messages using some kind of protocol.

  • connector client - it is used to find and connect to running connector server

JMX specification defines two protocols to be used between connector client and connector server:

  • jmxrmi - a protocol based on RMI (mandatory protocol for JMX implementations), uses JMX URIs in the form of service:jmx:rmi://…​

  • jmxmp - a protocol based directly on TCP sockets (optional protocol), uses JMX URIs in the form of service:jmx:jmxmp://…​

Java method to create and start connector server is javax.management.remote.JMXConnectorServerFactory.newJMXConnectorServer() and there’s only one standard implementation - javax.management.remote.rmi.RMIConnectorServer which supports RMI protocol and service:jmx:rmi://…​ JMX URIs

For RMI protocol, these JMX URIs are supported when creating a connector server:

  • service:jmx:rmi://host:port or service:jmx:rmi://host:port/ - after starting the connector server, a java.rmi.Remote object representing this server will be Base64 encoded and client will be able to access such server using service:jmx:rmi://host:port/stub/<base64 encoded javax.management.remote.rmi.RMIServer stub>

  • service:jmx:rmi://host:port/jndi/<path> - after starting the connector server, its RMI stub will be bound in JNDI registry under <path> name. The <path> is usually complex name in the form of rmi://rmi-registry-host:port/name

Knowing the above, the typical JMX URI of the remote MBean Server is:

service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi
Note
jmxrmi is a name explicitly used by sun.management.jmxremote.ConnectorBootstrap.exportMBeanServer() and the first host:port part are omitted becase we only care about the RMI registry where javax.management.remote.rmi.RMIServer is bound.

With this technology, what JVM actually exposes as a remote view of javax.management.remote.rmi.RMIServer interface which can be used to obtain javax.management.remote.rmi.RMIConnection interface which finally can be used to get/set MBean attributes, call MBean operations, etc.

Proxy connection to Remote JMX server

In the next example, a proxied Jolokia request queries the number of active threads for a Tomcat server via a proxy jolokia-proxy, which has an agent deployed under the context jolokia. The agent URL then is something like

http://jolokia-proxy:8080/jolokia

and the POST payload of the request is

[
  {
    "type": "read",
    "mbean": "Catalina:type=Server",
    "attribute": "serverInfo",
    "target": {
      "url": "service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi",
      "password":"admin",
      "user":"s!cr!t"
    }
  },
  {
    "type": "read",
    "mbean": "java.lang:type=Threading",
    "attribute": "ThreadCount",
    "target": {
      "url": "service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi",
      "password":"admin",
      "user":"s!cr!t"
    }
  }
]

The target is part of the request and can contain authentication information as well (with params user and password)

The result may look like:

[
  {
    "request": {
      "mbean": "Catalina:type=Server",
      "options": {
        "targetId": "service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi",
        "target": {
          "url": "service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi",
          "password":"admin",
          "user":"s!cr!t"
        }
      },
      "attribute": "serverInfo",
      "type": "read"
    },
    "value": "Apache Tomcat/10.1.16",
    "status": 200,
    "timestamp": 1701952860
  },
  {
    "request": {
      "mbean": "java.lang:type=Threading",
      "options": {
        "targetId": "service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi",
        "target": {
          "url": "service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi",
          "password":"admin",
          "user":"s!cr!t"
        }
      },
      "attribute": "ThreadCount",
      "type": "read"
    },
    "value": 36,
    "status": 200,
    "timestamp": 1701952860
  }
]

Limitations

Operating Jolokia as a JMX proxy has some limitations compared to a native agent deployment:

  • Bulk requests are possible but not as efficient as for direct operation. The reason is, that JSR-160 remoting doesn’t know about bulk requests, so that a Jolokia bulk request arriving at the proxy gets dispatched into multiple JSR-160 requests for the target. The JSR-160 remote connection has to be established only once, though.

  • The JMX target URL addresses the MBeanServer directly, so MBeanServer merging as it happens for direct operation is not available. Also, certain workarounds for bugs in the server’s JMX implementation are not available.

  • When not standard Java types are returned by JMX operations or attribute read calls, these types must be available on the proxy, too. Using the Jolokia agent directly, complex data types are serialized deeply into a JSON representation automatically.

  • For each Jolokia request, a new JMX connection (likely using RMI) is created which is an expensive operation. A future version of Jolokia will tackle this by providing some sort of optional JSR-160 connection pooling.

Next