Clients

Three client implementations exists for Jolokia: Jmx4Perl, the Perl binding (the grandmother of all clients ;-), a Java library and a JavaScript library. This reference describes the client bindings bundled with Jolokia. Information about Jmx4Perl can be found elsewhere.

JavaScript Client Library

Documentation for the previous version (pre-NPM, not based on TypeScript) of JavaScript library is available here: JavaScript Client Library (before ES modules).

Notable difference from previous version (pre 2.1.0) are:

  • The code is developed now using TypeScript language and processed using Rollup JS into ESM version (jolokia.mjs to be used with import statement) and UMD version (jolokia.js - to be used with browsers when not loading the script as type="module")

  • There’s no dependency on JQuery anymore

  • For HTTP requests, we use Fetch API.

  • There’s no support for ancient JSONP. CORS it the way to handle cross-origin requests.

  • Because Fetch API is used, there’s no direct support for synchronous requests - this can be achieved simply using async and await

  • There was an ajaxError option to handle HTTP/connection errors, but it was used to configure jQuery’s $.ajax call. Now Jolokia.request in Promise mode returns a Promise object and we can handle global errors using Promise.catch() handler where the passed object is a Response object.

  • In callback mode we can still pass success and error callbacks (to handle successfull and failed Jolokia responses - all delivered with HTTP 200 status code) and additionally we can pass fetchError (name was changed from ajaxError, because signature has changed as well) configuration option when creating new Jolokia object - this callback will be called with Response object to check for HTTP errors, etc. (when HTTP status is not 200) and/or with an error thrown by fetch() API call.

  • fetch() call may throw an exception (DOMException or TypeError) when networking error occurs or fetch() is configured in wrong way (for example with bad headers). Mind that it was a bit different with jQuery Ajax, where both HTTP error responses (like 404) and networking errors where handled with single callback being called with xhr instance. After switching to fetch() API, these errors in promise mode have to be handled in different way:

    • networking and configuration errors can be handled by attaching a .catch() handler to Jololokia.request() returned promise. The error will be a DOMException or TypeError

    • HTTP error responses (where status is different than 200) can be handled by attaching a .catch() handler, where the passed object will be a Response.

    • In callback mode, both the exception and failed Response objects can be passed to a callback configured with fetchError option.

All methods of this library are available via the Jolokia client object, which needs to be instantiated up-front. In the following example a client object is created and the used heap memory is requested synchronously via the simple API. The agent is deployed within the same web archive which also serves this script.

Here’s a sample code fragment that uses global Jolokia class which is used to create a jolokia instance used to invoke simple (from @jolokia.js/simple package) getAttribute() operation:

const jolokia = new Jolokia("/jolokia")
const value = await jolokia.getAttribute("java.lang:type=Memory", "HeapMemoryUsage", "used")
console.log("Heap Memory used: " + value)

Installation

The Jolokia JavaScript library is distributed in the form of npm packages which should be used as standard JavaScript libraries. It’s out of scope of this documentation to describe all possible bundler setups which may be used, let’s simply focus on two flavors of these libraries.

package.json for jolokia.js package contains this module-related section:

"exports": {
  ".": {
    "types": "./dist/dts/jolokia.d.ts",
    "import": "./dist/jolokia.mjs",
    "default": "./src/jolokia.ts"
  }
}

The recommended way to use this package is with JavaScript ES modules:

import Jolokia from "jolokia.js"

const jolokia = new Jolokia("/jolokia")
...

However if there’s a need to use Jolokia JavaScript libraries directly, it’s always possible to download relevant files (normal an minified) from download page. Then we can refer to the scripts from HTML page:

<head>
  <script src="jolokia-2.5.1.min.js"></script>
  <script src="jolokia-simple-2.5.1.min.js"></script>
</head>

A Jolokia client is always created as an instance of Jolokia. Requests to the agent are sent by calling methods on this object. The constructing function takes a plain object, which provides default parameters which are used in the request() if no overriding are given there.

Usage

All functions of this library are available as methods of the Jolokia object. The options argument needs to be instantiated as usual and it takes a set of default options, which can be overwritten by subsequent requests. On the most basic layer is a single request() method, which takes two arguments: A request object and an optional options object. For example, a request for obtaining the agent’s version for a agent running on the same server which delivered the JavaScript looks like:

let jolokia = new Jolokia({ url: "/jolokia" });
let response = await jolokia.request({ "type": "version" }, { "method": "post" });
console.log("Agent Version: " + response.value.agent);

If the constructor is used with a single string argument, this value is considered to be the agent’s access URL. I.e. in the example above the construction of the Jolokia could have been performed with a single string argument (new Jolokia("/jolokia")).

Requests

Jolokia requests and responses are represented as JSON objects. They have exactly the same format, which is expected and returned by the agent as defined in Jolokia Protocol for POST requests. All request types are supported.

The request() method expects as its first argument either a single request object or, for bulk requests, an array of request objects.

When Jolokia JavaScript library was using JQuery Ajax, there were two types of operations supported:

  • synchronous - when options object didn’t contain success callback

  • asynchronous - when user specified success callback in options object

Depending on this for synchronous operations either a single response JSON object is returned or an array of responses (in the order of the initial request array). For asynchronous request one or more callbacks are called for each response separately. See Operational modes for details.

With new Fetch API, there are more types of operations:

  • callback mode for compatibility purposes - you have to pass success and (optional) error callbacks through options object

  • recommended JSON method in promise mode when there’s no success callback passed and request() returns a Promise object:

    • for HTTP status 200, it is resolved with the response value, which may be successfull or error Jolokia responses containing JSON data with response or error details obtained from Response.json()

    • for HTTP status != 200, it throws an exception which is a Fetch Response object - user can attach .catch() to the promise and analyze the Response object for headers, status code, etc.

  • special Text method in promise mode when there’s no success callback, dataType: "text" is passed and request() returns a Promise object:

    • for HTTP status 200, it is resolved with the response value, which is plain text response obtained from Response.text().

    • for HTTP status != 200 it’s the same as in recommended JSON method

  • low level Response method in promise mode, when resolve: "response" is passed with options and request() returns a Promise object:

    • for any HTTP status code, the returned promise is resolved with entire Response object and user is free to call Response.json(), Response.text(), Response.blob() or any method from Response API.

Remember - when no success callback is passed, the returned Promise has to be configured for resolution:

  • by attaching .then() to get the value (both successful and error Jolokia responses - all within successful HTTP response (status == 200))

  • by attaching .catch() to catch the exceptions:

    • a Response object when there is some HTTP response

    • a DOMException or TypeError when there’s a networking or configuration error (see fetch exceptions)

  • by using await jolokia.request() to get the response

  • by surrounding with try..catch when there’s no .catch() used

For convenience, in both callback and promise modes, we can pass fetchError callback method in the options (in request() call or initially when creating Jolokia instance). Before Jolokia 2.1.0 this option was passed directly to $.ajax() call in jQuery. This callback has the following signature:

function(response, error)

and the parameters are:

  • response is a Response object when available

  • error is an exception object thrown by fetch() call (see fetch exceptions)

The following example shows a single and bulk request call to the Jolokia agent:

let jolokia = new Jolokia({ "url": "/jolokia" })
let req1 = { "type": "read", "mbean": "java.lang:type=Memory", "attribute": "HeapMemoryUsage" }
let req2 = { "type": "list" }
let response = await jolokia.request(req1)
let responses = await jolokia.request([ req1, req2 ])

Request options

Each request can be configured with a set of optional parameters provided either as default during construction of the Jolokia object or as optional last parameter for the request object. Also a request can carry a config attribute, which can be used for all processing parameters (Processing parameters). The known options are summarized in Table 1, “Request options”

Table 1. Request options
Key Description

url

Agent URL (mandatory)

method

Either "post" or "get" depending on the desired HTTP method (case does not matter). Please note, that bulk requests are not possible with "get". On the other hand, JSONP requests are not possible with "post" (which obviously implies that bulk request cannot be used with JSONP requests). Also, when using a read type request for multiple attributes, this also can only be sent as "post" requests. If not given, a HTTP method is determined dynamically. If a method is selected which doesn’t fit to the request, an error is raised.

dataType

The type of data specified to Jolokia request. The default value is json, and the response is parsed as JSON to an object. If the value is text, the response is returned as plain text without parsing. The client is then responsible for parsing the response. This can be useful when a custom JSON parsing is necessary. The value is returned as Promise’s resolution value.
Jolokia Simple API (jolokia-simple.js) doesn’t support text as dataType.
Added since jolokia.js 2.0.2

success

Callback function which is called for a successful request. The callback receives the response as single argument. If no success callback is given, then the request returns a Promise.

error

Callback in case a Jolokia error occurs. A Jolokia error is one, in which the HTTP request succeeded with a status code of 200, but the response object contains a Jolokia (not HTTP) status other than OK (200) which happens if the request JMX operation fails. This callback receives the full Jolokia response object (with a key error set). If no error callback is given, but success is available, the error response is printed to the JavaScript console by default.

fetchError

A callback invoked when fetch() returns a Response object with HTTP status different than 200 or simply throws an exception.

This callback can be specified both in callback and promise mode - both at request() time and Jolokia instantiation time.

credentials

This option is passed directly to fetch() call. See headers option.

username

A username used for HTTP authentication

password

A password used for HTTP authentication

timeout

Timeout for the HTTP request used with Abort Signal

headers

This option is passed directly to fetch() call.

maxDepth

Maximum traversal depth for serialization of complex return values

maxCollectionSize

Maximum size of collections returned during serialization. If larger, the collection is returned truncated.

maxObjects

Maximum number of objects contained in the response.

serializeLong

How to serialize long values in the JSON response: number or string. The default number simply serializes longs as numbers in JSON. If set to string, longs are serialized as strings. It can be useful when a JavaScript client consumes the JSON response, because numbers greater than the max safe integer don’t retain their precision in JavaScript.
Added since Jolokia 2.0.3

ignoreErrors

If set to "true", errors during JMX operations and JSON serialization are ignored. Otherwise if a single deserialization fails, the whole request returns with an error. This works only for certain operations like pattern reads.

canonicalNaming

Defaults to false for initiali property lists on object names; if set to "true" then canonical (sorted) order of properties will be used.

includeRequest

Whether the response object should contain related request object.
This option may be configured globally and overridden at request time. When false, bulk responses have to be correlated with requests by matching the requests using index number - responses come in the same order as requests.
Added since Jolokia 2.1.0

listKeys

Whether the response object for list() operation should contain keys field that lists all the keys obtained from each javax.management.ObjectName of the response. This may save you time parsing the name yourself.
Added since Jolokia 2.1.0

listCache

A flag (defaults to false) to enable optimized list response.
With this flag enabled, list() operation returns a bit different structure (that’s why we’ve upgraded protocol version to 8.0) where some MBeans may point to cached, shared MBeanInfo JSON fragment. This heavily decreases the size of list() response.
Added since Jolokia 2.1.0

serializeException

If true then in case of an error, the exception itself is returned in it JSON representation under the key error_value in the response object.

includeStackTrace

By default, a stacktrace is returned with every error (key: stacktrace) This can be omitted by setting the value of this option to false.

ifModifiedSince

The LIST operations provides an optimization in that it remembers, when the set of registered MBeans has been changes last. If a timestamp (in epoch seconds) is provided with this parameter, then the LIST operation returns an empty response (i.e. value is null) and a status code of 304 (Not Modified) if the MBeans haven’t changed. If you use the request scheduler (Table 1, “Request options”) then this feature can be used to get the callbacks called only if a value is returned. For the normal request, the error callback is called which must check the status itself.

Operational modes

Requests can be handled either with Promises or callbacks. If a success callback is given in the request options, the fetch() promise is handled by Jolokia. The callback gets these arguments:

  • a Jolokia JSON response object (see Requests and Responses)

  • an integer index indicating for which response this callback is being called.

For bulk requests, this index corresponds to the array index of the request which lead to this response. The value of this option can be an array of callback functions which are called in a round robin fashion when multiple responses are received in case of bulk requests. These callbacks are called only when the returned Jolokia response has a status code of 200, otherwise the callback(s) given with the error option are consulted. If no error callback is given, the error is printed on the console by default. As for success callbacks, error callbacks receive the Jolokia error response as a JSON object.

The following example shows callback-based requests for a single Jolokia request as well as for bulk request with multiple callbacks.

let jolokia = new Jolokia("/jolokia");

// Single request with a single success callback
jolokia.request(
  {
    "type": "read",
    "mbean": "java.lang:type=Memory",
    "attribute": "HeapMemoryUsage"
  },
  {
    "success": function(response) {
      if (response.value.used / response.value.max > 0.9) {
        console.info("90% of heap memory exceeded");
      }
    },
    "error": function(response) {
      console.info("Jolokia request failed: " + response.error);
    }
  }
);

// Bulk request with multiple callbacks
jolokia.request(
  [
    {
      "type": "read",
      "mbean": "java.lang:type=Threading",
      "attribute": "ThreadCount"
    },
    {
      "type": "read",
      "mbean": "java.lang:type=Runtime",
      "attribute": [ "VmName", "VmVendor" ]
    }
  ],
  {
    "success": [
      function(response) {
        console.log("Number of threads: " + response.value);
      },
      function(response) {
        console.log("JVM: " + response.value.VmName + " -- " + response.value.VmVendor);
      }
    ],
    "error": function(response) {
      alert("Jolokia request failed: " + response.error);
    }
  }
);

Both callbacks, success and error, are only called when HTTP response code is 200. In case of an error on the HTTP level It is the responsibility of user to attach .catch() to the returned Promise object. The passed function is called with the Response object, so we can get more information. For example:

const response = await jolokia.request({ type: "version" })
  .catch(r => {
      if (r.status <= 400) {
        // handle non-critical error
        console.warn(r.statusText)
      } else {
        // handle codes like 404 or 500
        console.warn("Critical error", r.statusText)
      }
  })

As explained in Requests and Responses the Jolokia agent supports two HTTP methods, GET and POST. POST is more powerful since it supports more features. e.g. bulk requests and JMX proxy requests are only possible with POST. By default, the Jolokia JavaScript library selects an HTTP method automatically, which is GET for simple cases and POST for more sophisticated requests. The HTTP method can be overridden by setting the option method to "get" or "post".

There are some limitations in choosing the HTTP method depending on the request and other options given:

  • Bulk requests (i.e. an array of multiple requests) can only be used with POST.

  • READ requests for multiple attributes (i.e. the attribute request parameter is an array of string values) can only be used with POST.

  • The JMX proxy mode (see Proxy Mode) can only be used with POST.

Using Promises

Without callbacks we can leverage full potential of promises.

The example we’ve shown to describe success and error callbacks can be written using promises:

let jolokia = new Jolokia("/jolokia");

// Single request with a single success callback
let response = await jolokia.request({
    "type": "read",
    "mbean": "java.lang:type=Memory",
    "attribute": "HeapMemoryUsage"
  }).catch(r => {
      console.info("Jolokia request failed: " + r.statusText);
  })
if (response && response.value.used / response.value.max > 0.9) {
    console.info("90% of heap memory exceeded")
}

Simple API

Building upon the basic Jolokia.request() method, a simplified access API is available. It is contained in jolokia-simple.js (from @jolokia.js/simple npm package) which must be included after jolokia.js. This API provides dedicated method for the various request types and supports all options as described in Table 1, “Request options”.

There is one notable difference for asynchronous callbacks and synchronous return values though: In case of a successful call, the callback is fed with the response’s value object, not the full response (i.e. response.value instead of response). Similar when a Promise is returned, it is resolved with the value itself from the response and not entire response.

There are also differences in error handling between simple and normal API:

  • for callback mode, the Jolokia error message is passed in the same way

  • for promise mode, if HTTP return code is 200, but it is a Jolokia error, instead of returning the error JSON message, its error field is thrown as an exception. HTTP error codes different that 200 are handled in the same way (by throwing or returning Fetch Response object)

Remember - to deal with values directly, await has to be used on the Promise returned from simple API.

getAttribute(mbean, attribute, path, opts)

This method returns the value of an JMX attribute attribute of an MBean mbean. A path can be optionally given, and the optional request options are given as last argument(s). The return value for synchronous operations are the attribute’s value, for callback operations (i.e. opts.success != null) it is null. See Reading attributes (read) for details.

For example, the following method call can be used to synchronously fetch the current heap memory usage:

let memoryUsed = await jolokia.getAttribute("java.lang:type=Memory", "HeapMemoryUsage", "used");
setAttribute(mbean, attribute, value, path, opts)

For setting an JMX attribute, this method takes the MBean’s name mbean, the attribute attribute and the value to set as value. The optional path is the inner path of the attribute on which to set the value (see Writing attributes (write) for details). The old value of the attribute is returned or given to a success callback.

To enable verbose mode in the memory-handling beans, use

let gsLoggingWasOn = await jolokia.setAttribute("java.lang:type=Memory", "Verbose", true);
execute(mbean, operation, arg1, arg2, …​, opts)

With this method, a JMX operation can be executed on the MBean mbean. Beside the operation’s name operation, one or more arguments can be given depending on the signature of the JMX operation. The return value is the return value of the operation. See Executing JMX operations (exec) for details.

The following exampled asynchronously fetches a thread dump as a JSON object and logs it into the console:

jolokia.execute("java.lang:type=Threading", "dumpAllThreads(boolean,boolean)", true, true, {
  "success": function(value) {
    console.log(JSON.stringify(value));
  }
});
search(mBeanPattern, opts)

Searches for one or more MBeans whose object names fit the pattern mBeanPattern. The return value is a list of strings with the matching MBean names or null if none is found. See Searching MBeans (search) for details.

The following example looks up all application servers available in all domains:

let servletNames = await jolokia.search("*:j2eeType=Servlet,*");
list(path, opts)

For getting meta information about registered MBeans, the list command can be used. The optional path points into this meta information for retrieving partial information. The format of the return value is described in detail in Listing MBeans (list).

This example fetches only the meta information for the attributes of the java.lang:type=OperatingSystem MBean:

let attributesMeta = await jolokia.list("java.lang/type=OperatingSystem/attr");
version(opts)

The version method returns the agent’s version, the protocol version, and possibly some additional server-specific information. See Getting the agent version (version) for more information about this method.

A sample return value for an Apache Tomcat server looks like:

{
  "agent": "2.5.1",
  "protocol": "8.0",
  "details": {
    "agent_version": "2.1.0-SNAPSHOT",
    "agent_id": "192.168.0.221-72527-6baa8838-servlet",
    "server_product": "tomcat",
    "server_vendor": "Apache",
    "server_version": "10.1.28",
    "secured": true,
    "url": "http://192.168.0.221:8080/jolokia"
  },
  "id": "192.168.0.221-72527-6baa8838-servlet",
  "config": {
    ...
  },
  "info": {
    "proxy": {},
    "jmx": {}
  }
}

Request scheduler

A Jolokia object can be also used for periodically sending requests to the agent. Therefore requests can be registered to the client object, and a poller can be started and stopped. All registered requests are send at once with a single bulk request so this is a quite efficient method for periodically polling multiple values.

Here is a simple example, which queries the heap memory usage every 10 seconds and prints out the used memory on the console:

let jolokia = new Jolokia("/jolokia")
handle = jolokia.register(function(resp) {
  console.log("HeapMemory used: " + resp.value);
},
{
  "type": "read",
  "mbean": "java.lang:type=Memory",
  "attribute": "HeapMemoryUsage", "path": "used"
});

jolokia.start(10000);
handle = jolokia.register(callback, request, request,…​.)

This method registers one or more request for being periodically fetched. callback can be either a function or an object.

If a function is given or an object with an attribute callback holding a function, then this function is called with all responses received as argument, regardless whether the individual response indicates a success or error state.

If the first argument is an object with two callback attributes success and error, these functions are called for each response separately, depending whether the response indicates success or an error state. If multiple requests have been registered along with this callback object, the callback is called multiple times, one for each request in the same order as the request are given. As second argument, the handle which is returned by this method is given and as third argument the index within the list of requests.

If the first argument is an object, an additional config attribute with processing parameters can be given which is used as default for the registered requests. Requests with a config section take precedence.

Furthermore, if a onlyIfModified: true exists in the callback object, then the success and error callbacks are called only if the result changed on the server side. Currently, this is supported for the list operation only in which case the callback is only called when MBean has been registered or deregistered since the last call of the scheduler. If a single callback function is used which gets all responses for a job at once, then this function is called only with the responses, which carry a value. If none of the registered requests produced a response with value (i.e. the server decided that there was no update for any request), then a call to the callback function is skipped completely.

register() returns a handle which can be used later for unregistering these requests.

In the following example two requests are registered along with a single callback function, which takes two responses as arguments:

handle = jolokia.register(function(resp1, resp2) {
  console.log("HeapMemory used: " + resp1.value);
  console.log("ThreadCount: " + resp2.value);
},
{
  "type": "read",
  "mbean": "java.lang:type=Memory",
  "attribute": "HeapMemoryUsage",
  "path": "used"
},
{
  "type": "read",
  "mbean": "java.lang:type=Threading",
  "attribute": "ThreadCount"
});

In the next example, a dedicated success and error callback are provided, which are called individually for each request (in the given order):

jolokia.register(
  {
    "success": function(resp) {
      console.log("MBean: " + resp.mbean + ", attr: " + resp.attribute + ", value: " + resp.value);
    },
    "error": function(resp) {
      console.log("Error: " + resp.error_text);
    },
    config: {
      "serializeException": true
    },
    "onlyIfModified": true
  },
  {
    "type": "list",
    "config": {
      "maxDepth": 2
    }
  },
  {
    "type": "read",
    "mbean": "java.lang:type=Threading",
    "attribute": "ThreadCount",
    "config": {
      "ignoreErrors": true
    }
  },
  {
    "type": "read",
    "mbean": "bla.blu:type=foo",
    "attribute": "blubber"
  }
);
jolokia.unregister(handle)

Unregister one or more requests registered with handle so that they are no longer polled with the scheduler.

jolokia.jobs()

Return an array of handles for all registered jobs. This array can be freely manipulated, its a copy of the handle list.

jolokia.start(period)

Startup the scheduler for requeting the agent every period milliseconds. If the scheduler is already running, it adapts its scheduling period according to the given argument. If no period is given, the period provided during construction time (with the option fetchInterval) is used. The default value is 30 seconds.

jolokia.stop()

Stop the scheduler. If the scheduler is not running, nothing happens. The scheduler can be restarted after it has been stopped.

jolokia.isRunning()

Checks whether the scheduler is running. Returns true if this is the case, false otherwise.

Java Client Library

This documentation section is related to the current Jolokia version. For previous Jolokia versions the chapter is available here: Java Client Library (Jolokia versions before 2.4.0)

The Java client library provides an easy access to the Jolokia Agent using Java API.
Sure, JSR-160, JMX Remote API also provides a Java based remote access to MBeans and one might wonder about the benefits of another Jolokia Java binding. There are several, though:

  • It provides a typeless access to remote MBeans. The big advantage is that for any non-OpenMBean access to custom typed objects is still possible without having the type information locally in the classpath.

  • Jolokia can be used in scenarios where JSR-160 connectors can not be used. I.e. in firewall secured environments it is much easier to get through to a Jolokia Agent using HTTP than to a JSR-160 connector using RMI as the transport protocol.

  • Remoteness is explicit in this API instead of JSR-160 connector’s seeked transparent remoteness. RMI has some arguable conceptual advantages, but hiding all the remote aspects proved to have quite some disadvantages when it comes to the programming model. Explicit awareness of a 'heavy-weight' remote call is better than false transparency in order to know the price tag.

The Java client library follows a strict request-response paradigm, much like the underlying HTTP. It uses generics heavily and can be centered around three classes:

  • org.jolokia.client.JolokiaClient is the client side class, which has various variants of an execute() methods for sending requests.

  • These methods take one or more org.jolokia.client.request.JolokiaRequest objects as arguments and

  • return one or more org.jolokia.client.request.JolokiaResponse objects as a result.

JolokiaClient SPI

Since the initial Jolokia release in 2010, Jolokia Client was implemented using Apache HttpClient 4.
This gives us 4 external dependencies: HttpClient4, HttpCore4, Commons Codec and Commons Logging.

JEP 321 introduced real HTTP Client API available in the JDK itself. java.net.HttpURLConnection was not suitable for more serious scenarios.

jolokia#565 was created in 2023, but it took a while to find a way to use JDK HTTP Client in Jolokia. While we could consider Apache HttpClient4 as deprecated and move entirely to JDK HTTP Client, it turned out that it’s possible to support both.
And more.

Jolokia 2.4.0 introduced a breaking API change (violating a bit the Semantic Versioning guidelines), because we wanted to achieve few goals:

  • rename J4pClient to JolokiaClient

  • keep the Jolokia Client API as a set of execute() methods that accept Jolokia request(s) and return Jolokia response(s)

  • remove strict dependency of JolokiaClient on particular HTTP Client implementation (for example org.apache.http.client.HttpClient was used as an argument of J4pClient constructor and J4pClientBuilder was accepting org.apache.http.client.CookieStore)

  • make JolokiaClient interface implementation-agnostic, but allow implementation-specific customization

Effectively we broke the API by renaming the class and removing the HttpClient4-specific arguments (renaming wouldn’t happen without the removal).

The goals were achieved by:

  • introducing org.jolokia.client.spi.HttpClientSpi SPI interface

  • providing default implementation of this SPI interface based on JDK HTTP Client

  • moving the implementation from Jolokia 2.3.0 to an SPI implementation based on Apache HttpClient 4

  • creating a new implementation based on Apache HttpClient 5

  • leveraging /META-INF/services/org.jolokia.client.spi.HttpClientBuilder Service Loader interface.

Now, starting from Jolokia 2.4.0, users can create and call Jolokia Client, while the particular HTTP Client implementation will be discovered automatically. And with default implementation (based on JDK HTTP Client) we no longer need any external dependencies.

Tutorial

Here is a sample application using JolokiaClient to get the amount of used heap memory:

import org.jolokia.client.JolokiaClient;
import org.jolokia.client.request.*;

public class MemoryDemo {
  public static void main(String[] args) {
    JolokiaClient client = new JolokiaClientBuilder().url("http://localhost:7778/jolokia")
        .user("jolokia")
        .password("jolokia")
        .build(); (1)
    JolokiaReadRequest request
        = new JolokiaReadRequest("java.lang:type=Memory", "HeapMemoryUsage"); (2)
    request.setPath("used"); (3)
    JolokiaReadResponse response = client.execute(request); (4)
    System.out.println("Memory used: " + response.getValue()); (5)
  }
}
1 JolokiaClient object is used using a JolokiaClientBuilder builder
2 A JolokiaReadRequest is created to specify an MBean name and its attribute to fetch
3 A path into the complex structure (here: java.lang.management.MemoryUsage) is specified
4 The request is sent using the Jolokia Client and a related JolokiaReadResponse is obtained
5 A value is printed from the response

In order to compile and run this example, we need only one library: jolokia-client-java-2.5.1.jar. See Download.

For Maven users, the following dependency is sufficient. It will use two transitive Jolokia dependencies:

  • jolokia-json-2.5.1.jar - for JSON serialization

  • jolokia-core-2.5.1.jar - for data conversion

<dependency>
    <groupId>org.jolokia</groupId>
    <artifactId>jolokia-client-java</artifactId>
    <version>2.5.1</version>
</dependency>

Using other HTTP Client implementation

If we want to use HTTP Client implementation from external library (instead of the one provided by the JDK itself), it’s enough to add one of these dependencies to the CLASSPATH:

Use Apache Http Client 4
<dependency>
    <groupId>org.jolokia</groupId>
    <artifactId>jolokia-client-java-httpclient4</artifactId>
    <version>2.5.1</version>
</dependency>
Use Apache Http Client 5
<dependency>
    <groupId>org.jolokia</groupId>
    <artifactId>jolokia-client-java-httpclient5</artifactId>
    <version>2.5.1</version>
</dependency>

JolokiaClient and JavaClientBuilder API

JolokiaClient is the entry point for sending requests to a remote Jolokia agent. It can be created in multiple ways. For simple cases, public constructors are provided taking the mandatory Jolokia agent URI and optionally an explicitly selected implementation of org.jolokia.client.spi.HttpClientSpi SPI interface (to skip the autodiscovery mechanism).

The recommended style is to use the JolokiaClientBuilder, though. This way, all the parameters for HTTP communication can easily be set:

JolokiaClient client = new JolokiaClientBuilder().url("http://localhost:7778/jolokia")
    .user("jolokia")
    .password("jolokia")
    .socketBufferSize(16384)
    .socketTimeout(5000)
    // ...
    .build();

The builder supports the following parameters with the given defaults:

Table 2. JolokiaClient parameters
Parameter Description Default

url

The URL to the Jolokia agent. This is the only mandatory parameter. Can be specified as a String or a java.net.URI object.

user

Username when authentication is used. If not set, no authentication is used. If set, password must be set, too

password

Password used for authentication. Only used when user is set.

target

A JMX JSR-160 ServiceURL which should be used by the agent as the real target. This parameter should be set if the client is used for accessing the agent in Proxy Mode.

targetUser

The JSR-160 user to use when using the proxy mode. If not given (and target is set), then no authentication is used for JSR-160 communication.

targetPassword

JSR-160 Password to use for the proxy mode.

connectionTimeout

The timeout in milliseconds until a connection is established. A timeout value of zero is interpreted as an infinite timeout.

20000

socketTimeout

Defines the socket timeout (SO_TIMEOUT) in milliseconds, which is the timeout for waiting for data or, put differently, a maximum period inactivity between two consecutive data packets. A timeout value of zero is interpreted as an infinite timeout.

0

pooledConnection

Specifies, that the underlying HttpClient should use pooled connection manager, which is thread safe and can service connection requests from multiples threads simultaneously. This is important if the JolokiaClient is to be used in a multi threaded context. The size of the pool is restricted by the parameter maxTotalConnection. ThreadSafeClientConnManager is the underlying connection manager. Pooled connections are the default.

singleConnection

Specifies that single connection should be used which maintains only one active connection at a time. Even though JolokiaClient is still thread-safe it ought to be used by one execution thread only. The underlying connection manager is SingleClientConnManager Pooled connections are the default.

maxTotalConnections

Defines the number of total connections to be pooled. It is only used when pooledConnection is used.

20

defaultMaxConnectionsPerRoute

Defines the number of total connections per route. It is only used when pooledConnection is used.

20

maxConnectionPoolTimeout

Defines the timeout for waiting to obtain a connection from the pool. This parameter is only used when pooledConnections are used.

500

contentCharset

Defines the charset to be used per default for encoding content body.

ISO-8859-1

expectContinue

Activates Expect: 100-Continue handshake for the entity enclosing methods. The purpose of the Expect: 100-Continue handshake to allow a client that is sending a request message with a request body to determine if the origin server is willing to accept the request (based on the request headers) before the client sends the request body. The use of the Expect: 100-continue handshake can result in noticeable performance improvement for entity enclosing requests that require the target server’s authentication.

true

tcpNoDelay

Determines whether Nagle’s algorithm is to be used. The Nagle’s algorithm tries to conserve bandwidth by minimizing the number of segments that are sent. When applications wish to decrease network latency and increase performance, they can disable Nagle’s algorithm (that is enable TCP_NODELAY). Data will be sent earlier, at the cost of an increase in bandwidth consumption.

true

socketBufferSize

Determines the size of the internal socket buffer in bytes used to buffer data while receiving and transmitting HTTP messages.

8192

proxy

Determines http proxy server. It can be defined as http://user:password@host:port. user and password are optional.

useProxyFromEnvironment

Set the proxy for this client based on http_proxy system environment variable. Expect formats are http://user:pass@host:port or http://host:port Example: http://tom:[email protected]:8080

responseExtractor

A response objectAccessor can be used for hooking into the JSON deserialization process when a JSON response is converted into a JolokiaResponse object. By default, the received JSON object is examined for a status code of 200 and only then creates a response object. Otherwise an exception is thrown. An objectAccessor is specified by the interface JolokiaResponseExtractor. Beside the default objectAccessor, an alternate objectAccessor ValidatingResponseExtractor can be used, which instead of throwing an exception returns a null object when the response has a status of 404. An objectAccessor can be specified as extra argument to the execute method, too.

org.jolokia.client.response.ValidatingResponseExtractor

defaultHttpHeaders

A map of default HTTP headers that should be sent with Jolokia requests.

keystore

A keystore with client credentials to be used with HTTPS connections. The keystore can be specified either as java.nio.file.Path or java.security.KeyStore

keystorePassword

Password to the keystore

keyPassword

Password to the Client key inside the keystore

truststore

A truststore with server certificates to be used with HTTPS connections. The truststore can be specified either as java.nio.file.Path or java.security.KeyStore

truststorePassword

Password to the truststore

protocolVersion

TLS protocol version to be used with HTTPS connections. When this option is not set, SSL configuration won’t be used. We can use for example TLSv1.3.

The JolokiaClient provides various variants of a execute() method, which take either one single request or a list of requests. For a single request, the preferred HTTP method (GET or POST) can be specified optionally. The List<R> argument type can be used only for a homogeneous bulk request, i.e. for multiple requests of the same time. Otherwise an untyped list must be used.

Each request can be tuned by giving a map of processing options along with their values to the execute() method. The possible options are specified using org.jolokia.client.JolokiaQueryParameter enum and are shown in the below table: Table 3, “JolokiaClient query parameters”.

Table 3. JolokiaClient query parameters
J4pQueryParameter enum Description

MAX_DEPTH

Maximum traversal depth for serialization of complex objects. Use this with a "list" request to restrict the depth of the returned meta data tree.

MAX_COLLECTION_SIZE

Maximum size of collections returned during serialization. If larger, a collection is truncated to this size.

MAX_OBJECTS

Maximum number of objects returned in the response’s value.

SERIALIZE_LONG

An option about how to serialize long values. We have an option to use number or string. This is more relevant for JavaScript, where the maximum value of a long type is different than in Java.

IGNORE_ERRORS

Option for ignoring errors during JMX operations and JSON serialization. This works only for certain operations like pattern reads and should be either true or false.

INCLUDE_STACKTRACE

Whether to include a stack trace in the response when an error occurs. The allowed values are true for inclusion, false if no stacktrace should be included or runtime if only RuntimeExceptions should be included. Default is true.

SERIALIZE_EXCEPTION

Whether to include a JSON serialized version of the exception. If set to true, the exception is added under the key error_value in the response. Default is false.

CANONICAL_NAMING

Whether property keys of ObjectNames should be ordered in the canonical way or in the way that they are created. The allowed values are either true in which case the canonical key order (== alphabetical sorted) is used or false for getting the keys as registered. Default is true

MIME_TYPE

Which MIME type to use with the response. only text/plain and application/json are supported. This is not very important for the Java Client.

INCLUDE_REQUEST

Whether the response object should contain related request object.
This option may be configured globally and overridden at request time. When false, bulk responses have to be correlated with requests by matching the requests using index number - responses come in the same order as requests.
Available since Jolokia 2.1.0

LIST_KEYS

Whether to enable org.jolokia.service.jmx.handler.list.ListKeysDataUpdater, so the object names are split into key=value pairs in the response

IF_MODIFIED_SINCE

Can be specified as milliseconds of the UNIX epoch for list requests. If there were no changes after this timestamp in the registered MBeans, a response with HTTP 304 return code is returned.

LIST_CACHE

Whether to use optimized version of the list response. It is critical for the scenarios where the Jolokia Agent has access to a lot of different MBeans of the same MBean interface.
Available since Jolokia 2.1.0

LIST_INTERFACES

Whether the list response should add information about all the interfaces implemented by MBeans. This options was added to support javax.management.MBeanServerConnection.isInstanceOf() method in jolokia-client-jmx-adapter
Available since Jolokia 2.5.0

OPEN_TYPES

Whether the list response should return information about javax.management.openmbean.OpenTypes used by the MBeans. This allows much better insight into the complex data structures used by the MBeans.
Available since Jolokia 2.5.0

Implementation specific configuration

Because Jolokia Client supports now (since version 2.5.0) 3 different implementations, we’ve added a way to configure implementation-specific details. For example the connection pool can be configured for Apache HttpClient using Java API, but for JDK HTTP Client, system properties are used.

We can pass a customizer to org.jolokia.client.JolokiaClientBuilder and here’s an example of calling Apache HttpClient5 specific methods when configuring Jolokia Client. We can also grab an instance of actual implementation:

import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.jolokia.client.JolokiaClient;
import org.jolokia.client.JolokiaClientBuilder;

public class App {

  public static void main(String[] args) throws Exception {
    JolokiaClient client = new JolokiaClientBuilder().url("http://localhost:8080/jolokia")
        .withCustomizer(HttpClientBuilder.class, (HttpClientBuilder builder) -> {
            builder.setUserAgent("My Client based on HttpClient 5");
            called[0] = true;
        }).build();
    HttpClient realClient = client.getHttpClient(HttpClient.class);
  }
}

Request types

For each request type a dedicated request object is provided which all are subclasses from JolokiaRequest. For all requests it can be specified which HTTP method is to be used by setting the property preferredHttpMethod to either GET or POST.

Each request type has a corresponding response type which used for the return values of the JolokiaClient.execute().

The constructor of each kind of request can take a JolokiaTargetConfig as argument for using a request in Proxy Mode. This configurational object holds the JMX service url and (optionally) credentials for JSR-160 authentication. When given, this proxy target specification overrides any default proxy configuration set during the initialization of the JolokiaClient.

JolokiaReadRequest and JolokiaReadResponse

JolokiaReadRequest is a read request to get one or more attributes from one or more MBeans within a single request. Various constructor variants can be used to specify one or more attributes along with the ObjectName (which can be a pattern). A path can be set as property for specifying an inner path, too.

JolokiaReadResponse is the corresponding response type and allows typed access to the fetched value for a single attribute fetch or to multiple values for a multi attribute read. In the latter case, the found object and attributes names can be retrieved as well.

For more information on fetching the value of multiple attributes and multiple MBeans at once, please refer to Reading attributes (read) or the Javadoc of JolokiaReadResponse.

JolokiaWriteRequest and JolokiaWriteResponse

A JolokiaWriteRequest is used to set the value of an MBean attribute. Beside the mandatory object and attribute name the value must be give in the constructor as well. Optionally a path can be provided, too. Only certain types for the given value can be serialized properly for calling the Jolokia agent as described in Request parameter serialization.

The old value is returned as JolokiaWriteResponse's value.

JolokiaExecRequest and JolokiaExecResponse

JolokiaExecRequest's are used for executing operation on MBeans. The constructor takes as mandatory arguments the MBean’s object name, the operation name and any arguments required by the operation. Only certain types for the given arguments can be serialized properly for calling the Jolokia agent as described in Request parameter serialization.

The returned JolokiaExecResponse contains the return value of the operation called.

JolokiaSearchRequest and JolokiaSearchResponse

A JolokiaSearchRequest contains a valid single MBean object name pattern which is used for searching MBeans.

The JolokiaSearchResponse holds a list of found object names.

JolokiaListRequest and JolokiaListResponse

For obtaining meta data on MBeans a JolokiaListRequest should be used. It can be used with a inner path to obtain only a subtree of the response, otherwise the whole tree as described in List response is returned. With the query parameter maxDepth can be used to restrict the depth of returned tree.

The single value of a JolokiaListResponse is a tree (or subtree) as a JSON object, which has the format described in List response.

JolokiaVersionRequest and JolokiaVersionResponse

A JolokiaVersionRequest request the Jolokia agent’s version information and takes no argument.

The JolokiaVersionResponse returns the agent’s version (agentVersion), the protocol version (protocolVersion), the application server product name (product), the vendor name (vendor) and any extra info (extraInfo) specific to the platform the Jolokia is running on.

Exceptions

In case of an error when executing a request a JolokiaException or one its subclass is thrown.

JolokiaConnectException

Exception thrown when the connection to the server fails. It contains the original java.net.ConnectException as nested value.

JolokiaTimeoutException

Exception thrown in case of an timeout. The nested exception depends on the HTTP Client implementation used, but should be an instance of java.io.IOException

JolokiaHttpException

Exception thrown in when the HTTP response is not delivered using HTTP status code 200. In such case, the response does not contain JSON body.

JolokiaRemoteException

Generic exception thrown when an exception occurred on the remote side. This is the case when the JSON response obtained is an error response as described in Responses. The error type, error value, the status, the request leading to this error and the remote stacktrace as string) can be obtained from this exception.

JolokiaBulkRemoteException

Exception thrown when a bulk request fails on the remote side. This contains a mixed list which contains the JolokiaRemoteException occurred as well as the JolokiaResponse objects for the requests, which succeeded. The list obtained by getResults() contains these objects in the same order as the list of requests given to execute. All responses and remote exceptions can also be obtained separately in homogeneous lists.

JolokiaException

Base exception thrown, when no other exception fits, i.e. when the exception happened on the client side. The original exception is contained as nested exception.

Jolokia JMX Connector Client

The JMX Remote Guide provides in-depth information about JSR-160 - the remote part of the JMX specification.
A JMX Connector is specified as a way to access a remote MBeanServer using javax.management.MBeanServerConnection interface, as if it was available locally. The default implementation shipped with standard JDK is based on RMI protocol and uses javax.management.remote.rmi.RMIServer (RMI) remote interface.

Jolokia Agent is an implementation of JMX Protocol Adaptor, but additionally provides a client part of the JMX Connector. This means we can access a remote JVM where a Jolokia agent is running using javax.management.MBeanServerConnection interface.
Internally invocations of MBeanServerConnection interface are translated into Jolokia Client library calls.

This allows you to use tools that leverage a JSR-160 MBeanServerConnection such as JConsole, Visual VM or Java Mission Control for connecting to a Jolokia endpoint.

Recommended plugins

For Java Mission Control, a set of plugins are provided at https://github.com/skarsaune/jmc-cloud-extensions/ and for Java Visual VM a Jolokia plugin can be downloaded from https://skarsaune.github.io/jolokia-jmc-update-site/visualvm/org-jolokia-jmx-plugin.nbm

Starting with version 9.1.0, Java Mission Control provides built-in Jolokia support.

Connecting to a Jolokia Agent using JSR-160 JMX Connector Client

Chapter Obtaining an RMI JMX connector (client) shows how the JMX code uses the protocol part of JMXServiceURL to locate a javax.management.remote.JMXConnectorProvider which is used to create a JMXConnector implementation. Refer to the entire JMX Remote Guide chapter for more details.

The example below shows how to connect programmatically to a Jolokia enabled Java process listening on localhost:8778 using the canonical JSR-160 API code.

For this example to work you need the following prerequisites:

  • Jolokia’s jolokia-client-jmx-adapter-2.5.1-standalone.jar must be on the classpath. See the Download page for the latest versions of this adapter jar.

  • Jolokia has to be accessible, running at http://localhost:8778/jolokia/. If user/password authentication is enabled for the agent, pass the credentials using javax.management.remote.JMXConnector.CREDENTIALS option (which is standard for remote JMX).

import java.util.HashMap;
import java.util.Map;

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class ConnectWithJolokiaDemo {
    public static void main(String[] args) throws Exception {
        Map<String, Object> options = new HashMap<>();
        // Add user & password if the Agent is secured:
        options.put(JMXConnector.CREDENTIALS, new String[] { "jolokia", "jolokia" });

        JMXConnector connector = JMXConnectorFactory.connect(
                new JMXServiceURL("service:jmx:jolokia+http://localhost:7778/jolokia"),
                options);

        MBeanServerConnection connection = connector.getMBeanServerConnection();
        CompositeData heapMemoryUsage = (CompositeData) connection
                .getAttribute(ObjectName.getInstance("java.lang:type=Memory"), "HeapMemoryUsage");
        System.out.println("Memory used: " + heapMemoryUsage.get("used"));

        connector.close();
    }
}

javax.management.MBeanServerConnection interface is a base interface for javax.management.MBeanServer with almost the same methods. However, as mentioned in JMX Remote RMI Connector - the details, the important differences are:

  • all the methods throw java.io.IOException to indicate that the call should be treated as remote (expect I/O exceptions)

  • there are no registerMBean() and unregisterMBean() methods

Connecting to a remote Jolokia Agent using JConsole

By providing an implementation of javax.management.remote.JMXConnector and javax.management.MBeanServerConnection, Jolokia can easily be used with tools that rely on these interfaces!

One of these tools is JConsole which is shipped with JDK distribution and is available in $JAVA_HOME/bin directory.

JConsole access remote JVM processes using a JMX Connector which is discovered by providing proper JMXServiceURL in this connection dialog:

jconsole connection dialog

Jolokia Agent may run on plain HTTP connection or may use HTTPS with or without certificate authentication. Before Jolokia 2.5.0 we could only use one form of JMXServiceURL: service:jmx:jolokia://<host>:<port>/<path> and HTTPS was used only if the port ended with 443.

Because RFC 2609 allows + sign in the resname component of the service URI, we’ve decided to support additional forms of Jolokia URIs:

  • service:jmx:jolokia://<host>:<port>/<path> - compatible with previous behavior - switching to HTTPS if the port ends with 443

  • service:jmx:jolokia+http://<host>:<port>/<path> - using HTTP explicitly

  • service:jmx:jolokia+https://<host>:<port>/<path> - using HTTPS explicitly

Of course for HTTPS connection we should be able to configure more than just the URL. Remember that Jolokia Client is a Java API, so developers using this API directly in their Java code can configure various aspects programmatically. With JConsole all we have is the command line, system properties and environment variables - and this is how Jolokia 2.5.0 and later allows to configure more options.

First - we need to tell JConsole how to find Jolokia JMX Connector classes and here’s how to do it:

$ jconsole -J-Djava.class.path=$HOME/.m2/repository/org/jolokia/jolokia-client-jmx-adapter/{jolokia-version}/jolokia-client-jmx-adapter-{jolokia-version}-standalone.jar
If you have problems with running JConsole (which is a Java Swing application) on Linux with Gtk L&F or on 4K monitor, please use these additional options: -J-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel -J-Dsun.java2d.uiScale=3

No additional options are needed when using HTTP.
This is enough to connect to remote JVM process and get all the information needed by JConsole! Jolokia JMX Connector properly translates all the complex types available in JMX MBeanServer, for example Flight Recorder:

jconsole connection dialog

TLS Configuration for JConsole and Jolokia JMX Connector

Traditionally the TLS configuration for Java uses stores of two types:

  • a keystore contains client certificate(s) and private key(s)

  • a truststore contains server issuer certificate(s) and/or Certificate Authority (CA) certificates

Java supports two kinds of keystore/truststore formats - proprietary JKS and standard PKCS#12.
However Jolokia (see https://github.com/jolokia/jolokia/issues/911) gives users more options - we can now specify certificates and keys in other formats:

  • X.509 certificates not embedded in Java keystores/truststores

  • PKCS#1 RSA keys

  • DSA keys

  • PKCS#8 RSA, DSA and EC keys

  • PKCS#5 (PBE) encrypted RSA, DSA and EC keys

  • all in either DER or PEM formats

We’ve introduced these system properties for Jolokia JMX Connector

System property Description

jolokia.keystore

Location (path) to a Java keystore with client certificate and private key

jolokia.keystorePassword

Password for the entire keystore

jolokia.keystoreAlias

Alias for the keystore for a private-key entry that should be used for client TLS authentication

jolokia.truststore

Location (path) to a Java keystore with server/ca data to validate the server

jolokia.truststorePassword

Password for the truststore

jolokia.clientCertificate

Location of a X.509 client certificate (PEM or DER) when not using a Java keystore

jolokia.clientKey

Location of PKCS#1 (RSA) or PKCS#8 private key (PEM or DER, encrypted or not) matching the client certificate

jolokia.clientKeyAlgorithm

Algorithm to be used for java.security.KeyFactory#getInstance() which usually can be deduced from PKCS#1/PKCS#8/PKCS#5 structure

jolokia.clientKeyPassword

Password for the private key specified separately or for a key inside the client keystore

jolokia.caCertificate

Location of a CA certificate (PEM or DER) when not using the truststore

jolokia.username

Username for Basic authentication

jolokia.password

Password for Basic authentication - mind that Basic Authentication should not be used without TLS

jolokia.connectionTimeout

Connection (establishment) timeout in milliseconds

jolokia.readTimeout

Read (socket) timeout in milliseconds

What’s more - all the above options can be specified as environmental variables too! We just have to switch to proper convention - for example jolokia.readTimeout system property name becomes JOLOKIA_READ_TIMEOUT environmental property name.

Here’s an example to run JConsole with TLS client authentication:

$ JOLOKIA_CLIENT_KEY_PASSWORD=jolokia \
    jconsole \
    -J-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel \
    -J-Dsun.java2d.uiScale=3 \
    -J-Djava.class.path=$HOME/.m2/repository/org/jolokia/jolokia-client-jmx-adapter/{jolokia-version}/jolokia-client-jmx-adapter-{jolokia-version}-standalone.jar \
    -J-Djolokia.caCertificate=ca.cer.pem \
    -J-Djolokia.clientCertificate=client.cer.pem \
    -J-Djolokia.clientKey=client-private.key-pk8.pem

Here, client-private.key-pk8.pem is a PEM file with a -----BEGIN ENCRYPTED PRIVATE KEY----- header and Jolokia can properly decrypt it using a password specified using JOLOKIA_CLIENT_KEY_PASSWORD environmental variable.

JSR-160 Connection to a JVM running in Kubernetes

If you are running a Jolokia-enabled JVM within a Kubernetes Pod, you can directly connect to this JVM from your local machine with the Jolokia JSR-160 connector. See the runnable example below for more details.

Before compiling and running the example below, please check that the following prerequisites are met:

  • Jolokia’s remote-jmx-adapter-2.5.1-javaagent.jar must be on the classpath. See the Download page for the latest versions of this adapter jar.

  • In addition, the Jolokia Kubernetes extension jolokia-kubernetes-2.5.1-javaagent.jar must be on the classpath. This can be also downloaded from the Download page.

  • Configuration and authentication for the Kubernetes context setup are as usual. I.e., you must be able to run kubectl with the cluster to connect to. You can check this by kubectl get namespace.

  • For the example below, we assume a Java process running in a Pod petclinic-6959c9b4cb-gk8np in namespace default that has a Jolokia agent enabled. This agent is supposed to listen on port 8778.

import javax.management.remote.*;
import javax.management.ObjectName;
import org.jolokia.kubernetes.client.KubernetesJmxConnector;

public class ConnectInKubernetesDemo {
  public static void main(String[] args) {
    Map options = new HashMap();
    // Add the Kubernetes context from the Kubernetes configuration that
    // points to your cluster. By default, the currently active context
    // is used.
    // options.put(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT,
    //             "docker-desktop");
    JMXConnector connector = JMXConnectorFactory.connect(
        new JMXServiceURL("service:jmx:kubernetes:///default/petclinic-6959c9b4cb-gk8np:8778/jolokia/"),
        options);
    connector.connect();
    System.out.println("Memory used: " +
        connector.getMBeanServerConnection().getAttribute(
            ObjectName.getInstance("java.lang:type=Memory"),"HeapMemoryUsage"));
  }
}

As an alternative to connecting programmatically to the Jolokia agent, you can also leverage jconsole to explore JMX on the remote JVM. You need to add the Jolokia connector client libraries when launching jconsole like in:

java -cp jolokia-agent-jvm-2.5.1-javaagent.jar:\
jolokia-client-jmx-adapter-2.5.1-standalone.jar:\
jolokia-client-kubernetes-2.5.1-standalone.jar \
-Djconsole.showOutputViewer sun.tools.jconsole.JConsole

After adding these Jolokia jars to sun.tools.jconsole.JConsole class we can then access remote server using Jolokia JMX URL:

jconsole
This page was built using the Antora default UI. The source code for this UI is licensed under the terms of the MPL-2.0 license. | Copyright © 2010 - 2025 Roland Huß