Jolokia Protocol
Jolokia uses a JSON-over-HTTP protocol which is described in this chapter. The communication is based on a request-response paradigm, where each HTTP request (which may contain multiple Jolokia requests) results in a single HTTP response (containing one Jolokia response for each incoming Jolokia request).
Jolokia requests can be sent in two ways: Either as a HTTP GET
request, in which case the request parameters are encoded
completely in the URL. Or as a POST request where the request is
put into a JSON payload in the HTTP request’s body.
GET based requests are mostly suitable for simple use cases and for
testing the agent via a browser. The focus here is on
simplicity.
POST based requests use a JSON representation of
the request within the HTTP body. They are more appropriate for
complex requests and provide some additional features (e.g. bulk
requests are only possible with POST).
The response returned by the agent always uses JSON for its data representation. It has the same format regardless whether GET or POST requests are used.
The rest of this chapter is divided into two parts: First, the general structure of requests and responses is explained after which the representation of Jolokia supported operations is defined.
Unfortunately the term operation is
used in different contexts which should be
distinguished from one another. Jolokia operations denote
the various kind of Jolokia requests (read, exec, …), whereas JMX
operations are methods which can be invoked on an JMX
MBean (as opposed to MBean attributes). Whenever the context requires it, this documents uses
Jolokia or JMX as prefix.
|
Requests and Responses
Jolokia knows about two different styles of handling requests, which are distinguished by the HTTP method used: GET or POST. Regardless of which method is used, the agent doesn’t keep any state on the server side (except of course that MBeans are obviously stateful most of the time). So in this aspect, the communication can be considered REST like[1].
Some Servlet API concepts
Before proceeding, let’s review some important concepts from Jakarta Servlet Specification 5 (and later).
Chapter 3.5. Request Path Elements splits full URI used to access the Java Servlet container into three parts (query string is not included here):
requestURI = contextPath + servletPath + pathInfo
These 4 components are accessed using these API calls:
-
jakarta.servlet.http.HttpServletRequest.getRequestURI() -
jakarta.servlet.http.HttpServletRequest.getContextPath() -
jakarta.servlet.http.HttpServletRequest.getServletPath() -
jakarta.servlet.http.HttpServletRequest.getPathInfo()- Request URI
-
this is simply a full URI without query string
- Context path
-
this fragment selects the web application (one of potentially many running in a single Servlet container). In Tomcat it is derived from the file name of a WAR archive in the
webapps/directory. It may be/(for specialROOT.waron Tomcat). - Servlet path
-
this is more complicated, as it indicates part of the URI that is used to select a single servlet within an individual web application. When the servlet mapping (
<servlet-mapping>/<url-pattern>element inWEB-INF/web.xml) is/pathor/path/*, servlet path is/path. Jolokia Agent WAR uses/*mapping, so servlet path is empty. - Path info
-
this is the URI part after the servlet path, so for Jolokia, it is simply everything after
/jolokia(when WAR agent is deployed to Tomcat aswebapps/jolokia.war).
GET requests
The simplest way to access the Jolokia agent is by sending
HTTP GET requests. These requests encode all their
parameters within the access URL. Jolokia uses
the path info part of an URL to extract the
parameters. Within the path info, each part is separated by
a slash (/). In general, the request URL
looks like this:
<base-url>/<type>/<arg1>/<arg2>/..../
The <base-url> specifies the URL
under which the agent is accessible and consists of protocol (http or https), host, port and
a context path.
It normally looks like
http://localhost:8080/jolokia, but depends on
your deployment setup.
The last part of such URL is the
context root of the deployed agent,
which by default is based on the agent’s filename
(e.g. jolokia.war).
<type> specifies one of the
supported Jolokia operations (described in the next
section), followed by one or more operation-specific
parameters separated by slashes.
For example, the following URL executes a
read Jolokia operation on the MBean
java.lang:type=Memory for reading the
attribute HeapMemoryUsage (see
Reading attributes (read)). It is assumed, that the agent is
reachable under the base URL
http://localhost:8080/jolokia:
http://localhost:8080/jolokia/read/java.lang:type=Memory/HeapMemoryUsage
Escaping rules in GET requests
GET requests are easy and straightforward - you can easily type them in browser address bar, bookmark or use with curl. However, there are limitations.
RFC 2396 and newer RFC 3986 specify what is and what is not allowed in the URL (which is a subset of URI). RFC 7230 specifies usage of URIs within the HTTP protocol.
As mentioned in Some Servlet API concepts, Jolokia uses path info to identify the type of operation to perform and its specific arguments (mbean names, attribute names for read/write operations or method name and arguments for exec operations).
RFC 3986 mentions that a URI:
-
path component with data organized in hierarchical form, where segments are separated by
/character (implicitly stating that/is forbidden as part of the segment) -
query component with data organized in non-hierarchical form
Also (and RFC 2396 says this explicitly, while newer RFC 3986 only highlights the convention), some characters in the segments may have special meaning (usually to implement the path parameters). These are ;, , and =. Finally, by the definition, ? separates the path part from the query part, so ? can’t be part of the path (and any segment of the path).
The problem is that mbean names or attribute values may use various characters which are illegal from the point of view of the URI or HTTP specifications.
That’s where Jolokia URI encoding steps in. In theory URI encoding should be sufficient, but it is not.
If one of the Jolokia request parts contain a slash
(/) (e.g. as part of you bean’s name, which is often the case with Camel or ActiveMQ) it
needs to be escaped. An exclamation mark
(!) is used as escape character [2].
An exclamation mark itself needs to be doubled
for escaping. Any other character preceded by an exclamation
mark is taken literally. Table
Table 1, “Escaping rules” illustrates the escape rules as
used in GET requests. Also, if quotes are part of an GET
request they need to be escaped with !".
| Escaped | Unescaped |
|---|---|
|
|
|
|
|
|
|
(anything else) |
For example, to read the attribute State
on the MBean named
jboss.jmx:alias=jmx/rmi/RMIAdaptor, an
access URL like this has to be constructed:
<base-url>/read/jboss.jmx:alias=jmx!/rmi!/RMIAdaptor/State
Client libraries like JMX::Jmx4Perl or Jolokia Client do this sort of escaping transparently.
Escaping can be avoided altogether if a slightly different
variant for a request is used (which doesn’t look that
REST-stylish, though). Instead of providing the information
as path-info, a query parameter p can be
used instead. This should be URL encoded, though. For the
example above, the alternative is
http://localhost:8080/jolokia?p=/read/jboss.jmx:alias=jmx%2Frmi%2FRMIAdaptor/State
This format must be used for GET
requests containing backslashes (\) since
backslashes can not be sent as part of an URL at all.
Summarizing, the recommended approach is to use POST requests if possible. If GET has to be used and escaping may be confusing with the server you used, use p query parameter. For GET requests with path segments, be careful about the escaping rules.
POST requests
POST requests are the most powerful and flexible way to communicate with the Jolokia agent. There are fewer escaping issues and it allows for features which are not available with GET requests. POST requests use a single Jolokia URL and put their payload within the HTTP request’s body. This payload is represented using JSON, a data serialization format originating from the JavaScript world.
The JSON format for a single request is a JSON object, which
is essentially a map with keys (or
attributes) and values. All requests
have a common mandatory attribute
type, which specifies the kind of Jolokia
operation to perform.
The other attributes are either
operation specific as described in
Jolokia operations or are processing
parameters which influence the overall behavior
and can be used with any request. See
Processing parameters for details.
Operation specific attributes
can be either mandatory or optional and depend on the operation type.
In the following examples, if not mentioned otherwise, attributes are mandatory.
Processing parameters are always optional, though.
A sample read request in JSON format looks like the
following example. It has a type
read and the three attributes:
mbean, attribute
and path which are specific to the read
request.
{
"type": "read",
"mbean": "java.lang:type=Memory",
"attribute": "HeapMemoryUsage",
"path": "used"
}
Each request JSON object results in a single JSON response object contained in the HTTP answer’s body. A bulk request contains multiple Jolokia requests within a single HTTP request. This is done by putting individual Jolokia requests into a JSON array:
[
{
"type": "read",
"attribute": "HeapMemoryUsage",
"mbean": "java.lang:type=Memory",
"path": "used"
},
{
"type": "search",
"mbean": "*:type=Memory,*"
}
]
This request will result in a JSON array result containing multiple JSON responses within the HTTP response. They are returned in same order as the requests in the initial bulk request.
Responses
All responses are delivered as HTTP responses, but there’s a clear distinction between HTTP and Jolokia error responses. Generally we have 3 kinds of responses:
-
successful response, which is always delivered with
HTTP 200(or in special case withHTTP 30x) code and contains JSON object (whether the request was a single request or a bulk request). -
error response delivered with
HTTP 200code - this kind of error is related to problems at JMX level, when everything that’s needed to perform a JMX operation is successfully extracted from the HTTP request. For these responses, JMX error may be delivered in a JSON response or a part of the JSON response when the bulk request was sent. This is called a Jolokia error response (as opposed to HTTP error response). -
error response delivered with
HTTP 4xx/5xxcodes - this happens when client sends invalid JSON (and Jolokia can’t even determine what should be done) or mismatched request parameters (for examplemaxDepth=fourty-two). When illegal parameters are used or the JSON is not well-formed, an HTTP 400 error is returned (Bad Request) indicating a user error. When there’s some issue at server side, HTTP 500 is returned.
Jolokia responses are always encoded in UTF-8 JSON format, whether the
request was a GET or POST request. In general, two kinds of
responses can be classified: In the normal case, an HTTP
Response with response code 200 is returned, containing the
result of the operation as a JSON payload. In case of an
error, a 4xx or 5xx code will be returned and the JSON
payload contains details about the error
occurred (e.g. 404 means "not found"). (See
this page
for more information about HTTP error codes..). If the processing option
ifModifiedSince is given and the requested
value has not changed since then, a response code of 304 is returned.
This option is currently only supported by the list request, for
other request types the value is always fetched.
In the non-error case a JSON response looks mostly the same
for each request type except for the
value attribute which depends on the operation being performed.
The format of a single Jolokia response is:
{
"value": ....,
"status" : 200,
"timestamp" : 1702391068,
"request": {
"type": ...,
....
},
"history": [
{
"value": ... ,
"timestamp" : 1702391069
},
....
]
}
For successful requests, the status is
always 200.
The timestamp contains the epoch
time[3]
when the
request has been handled.
The request for this
response can be found under the attribute
request (though this can be omitted if includeRequest=false parameter is sent).
Finally and optionally, if
history tracking is enabled (see
Tracking historical values), an entry with key
history contains a list of historical
values along with their timestamps. History tracking is only
available for certain type of requests
(read, write and
exec).
The value
is specific for the type of request, it can be a single
scalar value or a complex JSON structure.
If an error occurs, the status will be
a number different from 200. An error
response looks like this:
{
".error": true,
"error_type": "java.lang.UnsupportedOperationException",
"error_type_jmx": "javax.management.InstanceNotFoundException",
"error": "No type with name 'java.lang:type=Memory' exists",
"status": 400
}
For status codes it is important to distinguish status codes as they appear in the Jolokia JSON response objects and the HTTP status code of the (outer) HTTP response. There can be many Jolokia status codes, one for each Jolokia request contained in a single HTTP request. The HTTP status code merely reflect the status of the agent itself (i.e. whether it could perform the operation at all), whereas the Jolokia response status reflects the result of the operation (e.g. whether the performed operation throws an exception). So it is not uncommon to have an HTTP status code of 200, but the contained JSON response(s) indicating some errors.
I.e. the status has a code in the range
400 .. 499 or 500 .. 599
as it is specified for HTTP return codes.
The error JSON field contains an error
description. This is typically the message of an exception
occurred on the agent side[4].
error_type contains the Java class name
of the exception occurred and if the exception (or a wrapped exception) is a JMX exception,
it is explicitly put into error_type_jmx field.
The stacktrace contains a Java stacktrace
occurred on the server side (if any stacktrace is available and includeStackTrace option is set to true).
For each type of operation, the format of the
value entry is explained in
Jolokia operations
Paths
An inner path points to a certain substructure (plain value, array, map) within a complex JSON value. Think of it as something like "XPath lite". This is best explained by an example:
The attribute HeapMemoryUsage of the MBean
java.lang:type=Memory can be
requested with the URL
http://localhost:8080/jolokia/read/java.lang:type=Memory/HeapMemoryUsage
which returns a complex JSON structure like
{
"request": {
"type": "read"
"mbean": "java.lang:type=Memory",
"attribute": "HeapMemoryUsage",
},
"value": {
"init": 524288000,
"committed": 532676608,
"max": 8334082048,
"used": 27145000
},
"status": 200,
"timestamp": 1702392020
}
In order to get to the value for used heap memory you should
specify an inner path used, so that the
request
http://localhost:8080/jolokia/read/java.lang:type=Memory/HeapMemoryUsage/used
results in a response of 27145000 for the value:
{
"request": {
"path": "used",
"mbean": "java.lang:type=Memory",
"attribute": "HeapMemoryUsage",
"type": "read"
},
"value": 27145000,
"status": 200,
"timestamp": 1702392075
}
If the attribute contains arrays at some level, use a numeric index (0 based) as part of the inner path if you want to traverse into this array.
For both, GET and POST requests, path segments must be escaped as
described in Table 1, “Escaping rules” when they
contain slashes (/) or exclamation marks
(!).
Paths support wildcards * in a simple form. If given as a path part exclusively, it
matches any entry and path matching continues on the next level. This feature is especially
useful when using pattern read request together with paths. See Reading attributes (read) for details. A
* mixed with other characters in a path part has no special meaning and is used literally.
Jolokia operations
Reading attributes (read)
Reading MBean attributes is probably the most used JMX method, especially when it comes to monitoring. Concerning Jolokia, it is also the most powerful one with the richest semantics. Obviously the value of a single attribute can be fetched, but Jolokia supports also fetching of a list of given attributes on a single MBean or even on multiple MBeans matching a certain pattern.
Reading attributes are supported by both kinds of requests,
GET and POST.
Don’t confuse fetching multiple attributes on possibly multiple
MBeans with bulk requests. A single read request will always
result in a single read response, even when multiple attribute
values are fetched. Only the single response’s structure of the
value will differ depending on what kind of
read request was performed.
|
A read request for multiple attributes on the same MBean is initiated by giving a list of attributes to the request. For a POST request this is an JSON array, for a GET request it is a comma separated list of attribute names (where slashes and exclamation marks must be escaped as described in Escaping rules). If no attribute is provided, then all attributes are fetched. The MBean name can be given as a pattern in which case the attributes are read on all matching MBeans. If a MBean pattern and multiple attributes are requested, then only the value of attributes which matches both are returned, the others are ignored.
Paths can be used with pattern and multiple attribute read as well. In order to
skip the extra value levels introduced by a pattern read, the wildcard
* can be used. For example, a read request for the MBean Pattern
java.lang:type=GarbageCollector,* for the Attribute LastGcInfo
returns a complex structure holding information about the last garbage collection. If one is
looking only for the used memory during garbage collection, a path used could be used if
this request wouldn’t be a pattern request (i.e. refers a specific, single MBean).
But in this case since a
nested map with MBean and Attribute names is returned, the path */*/*/*/used has to be used
in order to skip the extra levels (for different heaps/spaces) for applying the path.
Note that in the following example the final value is not the full GC-Info but only the
value of its used entry for different spaces:
"value": {
"java.lang:name=G1 Young Generation,type=GarbageCollector": {
"LastGcInfo": {
"duration": 3,
"memoryUsageBeforeGc": {
"CodeHeap 'profiled nmethods'": 4780288,
"G1 Old Gen": 4934656,
"CodeHeap 'non-profiled nmethods'": 928256,
"G1 Survivor Space": 4194304,
"Compressed Class Space": 1331528,
"Metaspace": 12812816,
"G1 Eden Space": 20971520,
"CodeHeap 'non-nmethods'": 1271296
},
"GcThreadCount": 5,
"startTime": 786,
"endTime": 789,
"id": 2,
"memoryUsageAfterGc": {
"CodeHeap 'profiled nmethods'": 4780288,
"G1 Old Gen": 9082880,
"CodeHeap 'non-profiled nmethods'": 928256,
"G1 Survivor Space": 2597960,
"Compressed Class Space": 1331528,
"Metaspace": 12812816,
"G1 Eden Space": 0,
"CodeHeap 'non-nmethods'": 1271296
}
}
}
}
The following rule of thumb applies:
-
If a wildcard is used, everything at that point in the path is matched. The next path parts are used to match from there on. All the values on this level are included.
-
Every other path part is literally compared against the values on that level. If there is a match, this value is removed in the answer so that at the end you get back a structure with the values on the wildcard levels and the leaves of the matched parts.
-
If used with wildcards, paths behave also like filters. E.g. you can use a path
*/*/*/*/usedon the MBean patternjava.lang:*and get back only that portions which contains "used" as key, all others are ignored.
GET read request
The GET URL for a read request has the following format:
<base-url>/read/<mbean name>/<attribute name>/<inner path>
| Part | Description | Example |
|---|---|---|
|
The
ObjectName
of the MBean for which the attribute should be fetched. It
contains two parts: A domain part and a list of properties
which are separated by |
|
|
Name of attribute to read. This can be a list of Attribute names separated by comma. According to URI compliance, some special characters need to be escaped as described in Escaping rules. If no attribute is given, all attributes are read. |
|
|
This optional part describes an inner path as described in Paths |
|
With this URL the used heap memory can be obtained:
http://localhost:8080/jolokia/read/java.lang:type=Memory/HeapMemoryUsage/used
POST read request
A the keys available for read POST requests are shown in the following table.
| Key | Description | Example |
|---|---|---|
|
|
|
|
MBean’s ObjectName which can be a pattern |
|
|
Attribute name to read or a JSON array containing a list of attributes to read. No attribute is given, then all attributes are read. |
|
|
Inner path for accessing the value of a complex value (Paths) |
|
The following request fetches the number of active threads:
{
"type": "read",
"mbean": "java.lang:type=Threading",
"attribute": "ThreadCount"
}
Read response
The general format of the JSON response is described in Responses in detail. A typical response for an attribute read operation for a GET request with URL like:
http://localhost:8080/jolokia/read/java.lang:type=Memory/HeapMemoryUsage
{
"request": {
"mbean": "java.lang:type=Memory",
"attribute": "HeapMemoryUsage",
"type": "read"
},
"history": [
{
"value": {
"init": 524288000,
"committed": 532676608,
"max": 8334082048,
"used": 78027104
},
"timestamp": 1702454713
},
...
],
"value": {
"init": 524288000,
"committed": 532676608,
"max": 8334082048,
"used": 86415712
},
"status": 200,
"timestamp": 1702454822
}
- NOTE
-
Since Jolokia 2.1.0 we can use
includeRequestparameter to tell Jolokia to excluderequestfield from the response.
The value contains the response’s
value. For simple data types it is a scalar value, more complex
types are serialized into a JSON object. See
Object serialization for detail on object serialization.
For a read request of a single MBean with multiple attributes, the
returned value is a JSON object with the attribute names as keys
and their values as values. For example a request to
http://localhost:8080/jolokia/read/java.lang:type=Memory
leads to
{
"request": {
"mbean": "java.lang:type=Memory",
"type": "read"
},
"value": {
"ObjectPendingFinalizationCount": 0,
"Verbose": false,
"HeapMemoryUsage": {
"init": 524288000,
"committed": 532676608,
"max": 8334082048,
"used": 94804320
},
"NonHeapMemoryUsage": {
"init": 7667712,
"committed": 38928384,
"max": -1,
"used": 36905512
},
"ObjectName": {
"objectName": "java.lang:type=Memory"
}
},
"status": 200,
"timestamp": 1702454894
}
A request to a MBean pattern returns as value a JSON object,
with the MBean names as keys and as value another JSON object
with the attribute name as keys and the attribute values as
values. For example a request
http://localhost:8080/jolokia/read/java.lang:type=*/HeapMemoryUsage
returns something like
{
"request": {
"mbean": "java.lang:type=*",
"attribute": "HeapMemoryUsage",
"type": "read"
},
"value": {
"java.lang:type=Memory": {
"HeapMemoryUsage": {
"init": 524288000,
"committed": 532676608,
"max": 8334082048,
"used": 103192928
}
}
},
"status": 200,
"timestamp": 1702454978
}
Writing attributes (write)
Writing an attribute is quite similar to reading one, except that the request takes an
additional value element.
GET write request
Writing an attribute with a GET request, an URL with the following format has to be used:
<base url>/write/<mbean name>/<attribute name>/<value>/<inner path>
| Part | Description | Example |
|---|---|---|
|
MBean’s ObjectName |
|
|
Name of attribute to set |
|
|
The attribute name to value. The value must be serializable as described in Request parameter serialization. |
|
|
Inner path for accessing the parent object on which to set the value. (See also Paths). Note, that this is not the path to the attribute itself, but to the object carrying this attribute. With a given path it is possible to deeply set a value on a complex object. |
For example, you can set the garbage collector to verbose mode by using something like
http://localhost:8080/jolokia/write/java.lang:type=Memory/Verbose/true
POST write request
The keys which are evaluated for a POST write request are:
| Key | Description | Example |
|---|---|---|
|
|
|
|
MBean’s ObjectName |
|
|
Name of attribute to set |
|
|
The attribute name to value. The value must be serializable as described in Request parameter serialization. |
|
|
An optional inner path for specifying an inner object on which to set the value. See Paths for more on inner paths. |
Write response
As response for a write operation the old attribute’s value is returned. For a request
http://localhost:8080/jolokia/write/java.lang:type=ClassLoading/Verbose/true
you get the answer (supposed that verbose mode was switched off for class loading at the time this request was sent)
{
"request": {
"mbean": "java.lang:type=ClassLoading",
"attribute": "Verbose",
"type": "write",
"value": "true"
},
"value": false,
"status": 200,
"timestamp": 1702455595
}
- NOTE
-
Since Jolokia 2.1.0 we can use
includeRequestparameter to tell Jolokia to excluderequestfield from the response.
The response is quite similar to the read operation except for
the additional value element in the request
(and of course, the different type).
Executing JMX operations (exec)
With Jolokia we can also execute exposed JMX operations with optional arguments. Just as when writing attributes, Jolokia must be able to serialize the operation arguments. See Object serialization for details. Execution of overloaded methods is supported. The JMX specifications recommends to avoid overloaded methods when exposing them via JMX, though.
GET exec request
The format of an GET exec request is
<base url>/exec/<mbean name>/<operation name>/<arg1>/<arg2>/....
| Part | Description | Example |
|---|---|---|
|
MBean’s ObjectName |
|
|
Name of the operation to execute. If this is an overloaded method,
it is mandatory to provide a method signature as
well. A signature consist the fully qualified argument class
names or native types, separated by commas and enclosed with
parentheses. For calling a non-argument overloaded method use |
|
|
String representation for the arguments required to execute this operation. Only certain data types can be used here as described in Request parameter serialization. |
|
The following request will trigger a garbage collection:
http://localhost:8080/jolokia/exec/java.lang:type=Memory/gc
POST exec request
| Key | Description | Example |
|---|---|---|
|
|
|
|
MBean’s ObjectName |
|
|
The operation to execute, optionally with a signature as described above. |
|
|
An array of arguments for invoking this operation. The value must be serializable as described in Request parameter serialization. |
|
The following request dumps all threads (along with locked monitors and locked synchronizers, thats what the boolean arguments are for):
{
"type": "exec",
"mbean": "java.lang:type=Threading",
"operation": "dumpAllThreads(boolean, boolean)",
"arguments": [ true, true ]
}
Exec response
For an exec operation, the response
contains the return value of the
operation. null is returned if either the
operation returns a null value or the operation is declared as
void. A typical response for an URL like (mind that double quote (") has to be encoded with %22):
http://localhost:8080/jolokia/exec/java.util.logging:type=Logging/setLoggerLevel/%22%22/INFO
looks like
{
"request": {
"mbean": "java.util.logging:type=Logging",
"arguments": [
"",
"INFO"
],
"type": "exec",
"operation": "setLoggerLevel"
},
"value": null,
"status": 200,
"timestamp": 1702456520
}
- NOTE
-
Since Jolokia 2.1.0 we can use
includeRequestparameter to tell Jolokia to excluderequestfield from the response.
The return value get serialized as described in Request parameter serialization.
Searching MBeans (search)
With the Jolokia search operation the agent can be queried for
MBeans matching a given pattern. Searching will be performed on every
MBeanServer found by the agent.
GET search request
The format of the search GET URL is:
<base-url>/search/<pattern>
This mode is used to query for certain MBean. It takes a single
argument pattern for
specifying the search parameter like in
http://localhost:8080/jolokia/search/*:j2eeType=Servlet,*
You can use patterns as described
here,
i.e. it may contain wildcards like * and
?. The Mbean names matching the query
are returned as a list within the response.
POST search request
A search POST request knows the following keys:
| Key | Description | Example |
|---|---|---|
|
|
|
|
The MBean pattern to search for |
|
The following request searches for all MBeans registered in the
domain java.lang
{
"type": "search",
"mbean": "java.lang:*"
}
Search response
The answer is a list of MBean names which matches the pattern or an empty list if there was no match.
For example, the request
http://localhost:8888/jolokia/search/*:j2eeType=Servlet,*
{
"request": {
"mbean": "*:j2eeType=Servlet,*",
"type": "search"
},
"value": [
"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=Status",
"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=JMXProxy",
"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=jsp",
"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=HTMLManager",
"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/jolokia,j2eeType=Servlet,name=jsp",
"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/,j2eeType=Servlet,name=default",
"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/jolokia,j2eeType=Servlet,name=jolokia-agent",
"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/,j2eeType=Servlet,name=jsp",
"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/jolokia,j2eeType=Servlet,name=default",
"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=Manager",
"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=default"
],
"status": 200,
"timestamp": 1702458409
}
- NOTE
-
Since Jolokia 2.1.0 we can use
includeRequestparameter to tell Jolokia to excluderequestfield from the response.
The returned MBean names are properly quoted so that they can be directly used as input for other requests.
Listing MBeans (list)
The list operation collects information about available MBeans. This information includes the MBean names, their attributes, operations and notifications along with type information and description (as far as they are provided by the MBean author which doesn’t seem to be often the case).
GET list request
The GET request format for a Jolokia list request is
<base-url>/list/<inner path>
The <inner path>, as described in Paths
specifies a subset of the complete response. You can
use this to select a specific domain, MBean or
attribute/operation. See the next section for the format of the
complete response.
POST list request
A list POST request has the following keys:
| Key | Description | Example |
|---|---|---|
|
|
|
|
Inner path for accessing the value of a subset of the complete list Paths). |
|
The following request fetches the information about the MBean java.lang:type=Memory
{
"type": "list",
"path": "java.lang/type=Memory"
}
List response
The value has the following format:
{
"<domain>": {
"<prop list>": {
"attr": {
"<attr name>": {
"type": "<attribute type>",
"desc": "<textual description of attribute>",
"rw": "true|false"
},
...
},
"op": {
"<operation name>": {
"args": [
{
"type": "<argument type>",
"name": "<argument name>",
"desc": "<textual description of argument>"
},
...
],
"ret": "<return type>",
"desc": "<textual description of operation>"
},
...
},
"notif": {
"<notification type>": {
"name": "<name>",
"desc": "<desc>",
"types": [ "<type1>", "<type2>", ... ]
},
...
},
"class": "fully qualified class name",
"desc": "description",
"ctor": {
...
},
"interfaces": [
...
]
},
...
},
...
}
The domain name and the property
list together uniquely identify a single MBean. The
property list is in the so called canonical
order, i.e. in the form
"<key1>=<val1>,<key2>=<val2>,.."
where the keys are ordered alphabetically. Each MBean has zero
or more attributes and operations which can be reached in an
MBeans JSON object with the keys attr and
op respectively. Within these groups the
contained information is explained above in the schema and
consist of Java types for attributes, arguments and return
values, descriptive information and whether an attribute is
writable (rw == true) or
read-only.
As for reading attributes you can fetch a subset of this information using an
path. E.g a path of domain/prop-list would return the value for a single
bean only. For example, a request
http://localhost:8080/jolokia/list/java.lang/type=Memory
results in an answer
{
"request": {
"path": "java.lang/type=Memory",
"type": "list"
},
"value": {
"op": {
"gc": {
"args": [],
"ret": "void",
"desc": "gc"
}
},
"notif": {
"javax.management.Notification": {
"types": [
"java.management.memory.threshold.exceeded",
"java.management.memory.collection.threshold.exceeded"
],
"name": "javax.management.Notification",
"desc": "Memory Notification"
}
},
"attr": {
"ObjectPendingFinalizationCount": {
"r": true,
"rw": false,
"w": false,
"is": false,
"type": "int",
"desc": "ObjectPendingFinalizationCount"
},
"Verbose": {
"r": true,
"rw": true,
"w": true,
"is": true,
"type": "boolean",
"desc": "Verbose"
},
"HeapMemoryUsage": {
"r": true,
"rw": false,
"w": false,
"is": false,
"type": "javax.management.openmbean.CompositeData",
"desc": "HeapMemoryUsage"
},
"NonHeapMemoryUsage": {
"r": true,
"rw": false,
"w": false,
"is": false,
"type": "javax.management.openmbean.CompositeData",
"desc": "NonHeapMemoryUsage"
},
"ObjectName": {
"r": true,
"rw": false,
"w": false,
"is": false,
"type": "javax.management.ObjectName",
"desc": "ObjectName"
}
},
"class": "sun.management.MemoryImpl",
"desc": "Information on the management interface of the MBean"
},
"status": 200,
"timestamp": 1770643662
}
- NOTE
-
Since Jolokia 2.1.0 we can use
includeRequestparameter to tell Jolokia to excluderequestfield from the response.
Restrict the depth of the returned tree
The optional parameter maxDepth can be used
to restrict the depth of the return tree. Two value are
possible: A maxDepth of 1 restricts the
return value to a map with the JMX domains as keys, a
maxDepth of 2 truncates the map returned to
the domain names (first level) and the MBean’s properties
(second level). The final values of the maps don’t have any
meaning and are dummy values.
Extension points
When returning list() results, Jolokia translates each MBean’s javax.management.MBeanInfo information into a JSON fragment. Standard fields of this fragment are:
-
class- information about the class of an MBean -
desc- the description -
attr- attributes of an MBean -
op- operations of an MBean -
notif- notifications supported by an MBean -
ctor- constructors of an MBean
These fields are added by default, built-in implementations of org.jolokia.server.core.service.api.DataUpdater.
Since Jolokia 2.1.0 we can now discover (using /META-INF/jolokia/services) additional services of org.jolokia.server.core.service.api.DataUpdater class which can be used to construct (or override) additional fields of MBean’s JSON information.
Potential use-case may be information related to RBAC (Role-based Access Control).
One additional built-in data updater is org.jolokia.service.jmx.handler.list.ListKeysDataUpdater which can be enabled using listKeys=true processing parameter. We can use it to get additional "keys" MBeanInfo containing keys obtained from MBean’s ObjectName. For example:
{
"request": {
"path": "java.lang",
"type": "list"
},
"value": {
"name=G1 Survivor Space,type=MemoryPool": {
...
"keys": {
"name": "G1 Survivor Space",
"type": "MemoryPool"
},
...
Since Jolokia 2.5.0, we can use listInterfaces=true parameter to get information about the interfaces implemented by an MBean:
{
"request": {
"path": "java.lang/type=Memory",
"type": "list"
},
"value": {
...
"interfaces": [
"javax.management.NotificationEmitter",
"java.lang.management.PlatformManagedObject",
"javax.management.NotificationBroadcaster",
"java.lang.management.MemoryMXBean"
],
...
Another big addition to Jolokia 2.5.0 is new processing parameter. When sending openTypes=true, we get full information about
the composite/tabular Open Types used by the MBean (attributes or method parameters and return values). For example:
{
"request": {
"path": "java.lang/type=Memory",
"type": "list"
},
"value": {
...
"attr": {
"ObjectPendingFinalizationCount": {
"r": true,
"rw": false,
"w": false,
"is": false,
"type": "int",
"desc": "ObjectPendingFinalizationCount",
"openType": "java.lang.Integer"
},
"Verbose": {
"r": true,
"rw": true,
"w": true,
"is": true,
"type": "boolean",
"desc": "Verbose",
"openType": "java.lang.Boolean"
},
"HeapMemoryUsage": {
"r": true,
"rw": false,
"w": false,
"is": false,
"type": "javax.management.openmbean.CompositeData",
"desc": "HeapMemoryUsage",
"openType": {
"kind": "composite",
"type": "java.lang.management.MemoryUsage",
"class": "javax.management.openmbean.CompositeData",
"items": {
"init": "java.lang.Long",
"committed": "java.lang.Long",
"max": "java.lang.Long",
"used": "java.lang.Long"
},
"desc": "java.lang.management.MemoryUsage"
}
},
...
In the above example, we can see the actual type structure of HeapMemoryUsage attribute.
Optimized List response
Since Jolokia 2.1.0 we provide now listCache request processing parameter. When this parameter is set to true (it’s false for backward compatibility), list() response has different format - instead of a structure like:
domain:
mbean:
op:
attr:
notif:
class:
desc:
...
...
we now have:
"domains":
domain:
mbean: cache-key
...
...
"cache":
cache-key:
op:
attr:
notif:
class:
desc:
...
Effectively:
-
domain → mbean tree is moved 1 level down under
"domains"field oflist()response -
mbean may contain known
op,attr, … fields, but may also be just a cache key pointing toop,attr, … data stored under this key under"cache"field oflist()response -
the cache keys are generated by
org.jolokia.service.jmx.api.CacheKeyProviderservices/extensions
Using JMX notifications (notification)
A new feature of Jolokia 2 is access to JMX notifications. While reading/writing attributes, executing operations or listing/searching MBeans is implemented as single request-response operation, with notifications the flow of messages is more complex.
There are 4 groups of subcommands for Jolokia notification operation:
-
Client registration/unregistration:
registerandunregistercommands -
Adding/removing/listing the listeners:
add,removeandlistcommands -
Ping (to refresh the registered client):
pingcommand -
Configuring a channel to a stream of notifications:
opencommand
Client registration
In order to subscribe to JMX notification, a client has to be registered, so Jolokia agent can be aware of the entities for which notifications should be collected and returned.
The GET URL for client registration has the following format:
<base-url>/notification/register
The equivalent POST JSON payload is:
{
"type": "notification",
"command": "register"
}
There are no additional parameters in GET URL request or POST JSON payload.
The general format of the JSON response is described in Responses in detail. A typical response for client registration is:
{
"request": {
"type": "notification",
"command": "register"
},
"value": {
"backend": {
"pull": {
"maxEntries": 100,
"store": "jolokia:type=NotificationStore,agent=192.168.0.221-21185-7e985ce9-servlet"
},
"sse": {
"backChannel.contentType": "text/event-stream",
"backChannel.encoding": "UTF-8"
}
},
"id": "d77475dc-c7a7-4f71-b988-52b7f0252ca3"
},
"status": 200,
"timestamp": 1702464211
}
- NOTE
-
Since Jolokia 2.1.0 we can use
includeRequestparameter to tell Jolokia to excluderequestfield from the response.
The value field in the response contains two important fields:
-
idis an identifier of registered client, which is used in other notification-related Jolokia operations -
backendis a collection of available backends (See more in Accessing notification stream). Jolokia 2 supportssseandpullbackends. Other backends (likewebsocket) may be added in the future.pullbackend-
In this implementation, notifications are collected within the Jolokia Agent and client has to fetch (pull) them by calling
pulloperation onjolokia:type=NotificationStoreMBean. ssebackend-
In this mode Server Sent Events are used (See WhatWG specification).
Client unregistration
In order to unsubscribe from Jolokia notification mechanism, an existing client has to be unregistered, by passing an existing client ID.
The GET URL for client registration has the following format:
<base-url>/notification/unregister/<client-id>
| Part | Description | Example |
|---|---|---|
|
Client ID of previously registered client |
|
The equivalent POST JSON payload is:
{
"type": "notification",
"command": "unregister",
"client": "<client-id>"
}
| Key | Description | Example |
|---|---|---|
|
|
|
|
|
|
|
Client ID of previously registered client |
|
A typical response for client unregistration is:
{
"request": {
"client": "d77475dc-c7a7-4f71-b988-52b7f0252ca3",
"type": "notification",
"command": "unregister"
},
"value": null,
"status": 200,
"timestamp": 1702464913
}
The returned value is simply null.
Adding a notification listener
Having registered a notification client, we can now use notification listeners. The underlying JMX call is javax.management.MBeanServerConnection.addNotificationListener() method which requires several parameters:
-
ObjectName: The name of the MBean on which the listener should be added -
NotificationListener: The listener object which will handle the notifications emitted by the registered MBean. -
NotificationFilter: The filter object. It can be used to filter notifications specific to a given MBean -
Object: any handback object which will be passed to a listener when notification arrives - this is how Jolokia can get back to the client which added a JMX notification listener.
The GET URL for adding a notification listener has the following format:
<base-url>/notification/add/<client-id>/<mode>/<mbean name>/<filter1>,.../<config>/<handback>
| Part | Description | Example |
|---|---|---|
|
Client ID of previously registered client |
|
|
One of supported modes of notification handling: |
|
|
The ObjectName of the MBean for which we’re registering a notification listener |
|
|
Comma-separated list notifications we’re interested in (and supported by given |
|
|
This optional part can be passed to a notification listener as JSON object |
|
|
This optional part can be passed to a notification listener and will be returned for each related notification. In GET request it can only be a String value. |
|
The equivalent POST JSON payload is:
{
"type": "notification",
"command": "add",
"client": "1cddf91c-423e-46d8-ac9a-2eb6d8b213c7",
"mode": "pull",
"mbean": "JMImplementation:type=MBeanServerDelegate",
"filter": [],
"handback": "id-1234"
}
| Key | Description | Example |
|---|---|---|
|
|
|
|
|
|
|
Client ID of previously registered client |
|
|
One of supported modes of notification handling: |
|
|
The ObjectName of the MBean for which we’re registering a notification listener |
|
|
A JSON array of notification notifications we’re interested in (and supported by given |
|
|
This optional part can be passed to a notification listener as JSON object |
|
|
This optional part can be passed to a notification listener and will be returned for each related notification. |
|
A typical response for added notification listener is:
{
"request": {
"mode": "pull",
"mbean": "JMImplementation:type=MBeanServerDelegate",
"client": "1cddf91c-423e-46d8-ac9a-2eb6d8b213c7",
"type": "notification",
"handback": "id-1234",
"command": "add"
},
"value": "2",
"status": 200,
"timestamp": 1702472334
}
The returned value is a handle to the added listener, required when removing the listener in the future.
Checking existing notification listeners
To check existing listener registrations for previously registered client, we can use list command of notification operation.
The GET URL for listing client listener registrations has the following format:
<base-url>/notification/list/<client-id>
| Part | Description | Example |
|---|---|---|
|
Client ID of previously registered client |
|
The equivalent POST JSON payload is:
{
"type": "notification",
"command": "list",
"client": "<client-id>"
}
| Key | Description | Example |
|---|---|---|
|
|
|
|
|
|
|
Client ID of previously registered client |
|
A typical response for listing the registrations is:
{
"request": {
"client": "1cddf91c-423e-46d8-ac9a-2eb6d8b213c7",
"type": "notification",
"command": "list"
},
"value": {
"1": {
"mbean": "JMImplementation:type=MBeanServerDelegate",
"handback": "id-1234"
},
"2": {
"filter": [
"java.management.memory.threshold.exceeded"
],
"mbean": "java.lang:type=Memory"
}
},
"status": 200,
"timestamp": 1702472848
}
The returned value is a collection of listener registrations with their details, keyed by handle id.
Removing a notification listener
When a notification listener for a given client is no longer needed, we can remove using remove command of notification operation.
The GET URL for removing client listener registrations has the following format:
<base-url>/notification/remove/<client-id>/<handle>
| Part | Description | Example |
|---|---|---|
|
Client ID of previously registered client |
|
|
A handle of previously added listener |
|
The equivalent POST JSON payload is:
{
"type": "notification",
"command": "remove",
"client": "<client-id>",
"handle": "<handle-id>"
}
| Key | Description | Example |
|---|---|---|
|
|
|
|
|
|
|
Client ID of previously registered client |
|
|
A handle of previously added listener |
|
A typical response for listing the registrations is:
{
"request": {
"client": "1cddf91c-423e-46d8-ac9a-2eb6d8b213c7",
"handle": "1",
"type": "notification",
"command": "remove"
},
"value": null,
"status": 200,
"timestamp": 1702473703
}
The returned value is simply null.
Accessing notification stream
When a listener is added for a client (see Adding a notification listener), a mode indicates a desired notification backend.
For pull backend, there’s actually no back channel over which we can receive the notifications. Instead we should be
calling an MBean operation on an MBean representing the backend. The details are available when client was first registered:
"backend": {
"pull": {
"maxEntries": 100,
"store": "jolokia:type=NotificationStore,agent=192.168.0.221-21185-7e985ce9-servlet"
},
...
If we want to access notifications collected in pull backend, we have to call org.jolokia.service.notif.pull.PullNotificationStoreMBean.pull(String pClientId, String pHandle) MBean operation on jolokia:type=NotificationStore,agent=<agent-id> MBean which can be done with Jolokia exec operation.
For example, having registered a notification listener for JMImplementation:type=MBeanServerDelegate MBean, we can get notified about MBean registrations/unregistrations. Accessing the pull notification store can be done with exec operation like this:
$ curl -s -u jolokia:jolokia 'http://localhost:8080/jolokia/exec/jolokia:type=NotificationStore,agent=192.168.0.221-21185-7e985ce9-servlet/pull(java.lang.String,java.lang.String)/1cddf91c-423e-46d8-ac9a-2eb6d8b213c7/2' | jq .
{
"request": {
"mbean": "jolokia:agent=192.168.0.221-21185-7e985ce9-servlet,type=NotificationStore",
"arguments": [
"1cddf91c-423e-46d8-ac9a-2eb6d8b213c7",
"2"
],
"type": "exec",
"operation": "pull(java.lang.String,java.lang.String)"
},
"value": {
"dropped": 0,
"handle": "2",
"handback": "id-1234",
"notifications": [
{
"timeStamp": 1702473332222,
"sequenceNumber": 248,
"userData": null,
"mBeanName": {
"objectName": "Catalina:name=HttpRequest3,type=RequestProcessor,worker=\"http-nio-8080\""
},
"source": {
"objectName": "JMImplementation:type=MBeanServerDelegate"
},
"message": "",
"type": "JMX.mbean.registered"
},
{
"timeStamp": 1702473390407,
"sequenceNumber": 249,
"userData": null,
"mBeanName": {
"objectName": "Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/,name=jsp,type=JspMonitor"
},
"source": {
"objectName": "JMImplementation:type=MBeanServerDelegate"
},
"message": "",
"type": "JMX.mbean.unregistered"
},
...
On the other hand, sse notification store works differently. Here’s the information received during client registration:
"sse": {
"backChannel.contentType": "text/event-stream",
"backChannel.encoding": "UTF-8"
}
Instead of providing us with Mbean name to access when needed (pull the notifications out if it by calling an MBean operation), sse backend needs a channel associated with client connection. This is where open command for notification operation comes into play.
When calling open command for sse backed notifications, the request (HttpServletRequest) is put into asynchronous mode and connection is not closed.
The GET URL for opening a backend channel for notification access is:
<base-url>/notification/open/<client-id>/<mode>
| Part | Description | Example |
|---|---|---|
|
Client ID of previously registered client |
|
|
A mode of notification delivery. Only |
|
The equivalent POST JSON payload is:
{
"type": "notification",
"command": "open",
"client": "<client-id>",
"mode": "<mode>"
}
| Key | Description | Example |
|---|---|---|
|
|
|
|
|
|
|
Client ID of previously registered client |
|
|
A mode of notification delivery. Only |
|
The notifications are returned to the client as they’re delivered from JMX. Here’s a sample interaction:
$ curl -i -u jolokia:jolokia 'http://localhost:8080/jolokia/notification/open/c72e2f07-e5ec-47a0-b9b4-3036b16614a0/sse'
HTTP/1.1 200
Cache-Control: private
Content-Type: text/event-stream;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 13 Dec 2023 15:49:39 GMT
:
:
:
:
:
id: 363
data: {"dropped":0,"handle":"2","handback":null,"notifications":[{"timeStamp":1702482067031,"sequenceNumber":363,"userData":null,"mBeanName":{"objectName":"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=\/\/localhost\/,name=jsp,type=JspMonitor"},"source":{"objectName":"JMImplementation:type=MBeanServerDelegate"},"message":"","type":"JMX.mbean.unregistered"}]}
id: 364
data: {"dropped":0,"handle":"2","handback":null,"notifications":[{"timeStamp":1702482067031,"sequenceNumber":364,"userData":null,"mBeanName":{"objectName":"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=\/\/localhost\/,j2eeType=Filter,name=Tomcat WebSocket (JSR356) Filter"},"source":{"objectName":"JMImplementation:type=MBeanServerDelegate"},"message":"","type":"JMX.mbean.unregistered"}]}
The returned data is structured according to text/event-stream Mime type. id and data fields are used, where id matches the sequenceNumber from the JSON payload.
Getting the agent version (version)
The Jolokia command version returns the version of
the Jolokia agent along with the protocol version.
GET version request
The GET URL for a version request has the following format:
<base-url>/version
For GET request the version part can be
omitted since this is the default command if no command is
provided as path info.
POST version request
A version POST request has only a single key
type which has to be set to
version.
Version response
The response value for a version request looks like:
{
"request": {
"type": "version"
},
"value": {
"agent": "2.5.0",
"protocol": "8.2",
"decorators": {},
"details": {
"agent_version": "2.5.0",
"agent_id": "jolokia-7778",
"secured": false,
"url": "http://192.168.0.166:7778/jolokia"
},
"id": "jolokia-7778",
"config": {
"maxDepth": "15",
"discoveryEnabled": "true",
"maxCollectionSize": "0",
"agentId": "jolokia-7778",
"debug": "true",
"canonicalNaming": "false",
"dateFormat": "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX",
"agentContext": "/jolokia",
"historyMaxEntries": "10",
"maxObjects": "0",
"debugMaxEntries": "100"
},
"info": {
"proxy": {},
"jmx": {}
}
},
"status": 200,
"timestamp": 1770638947
}
- NOTE
-
Since Jolokia 2.1.0 we can use
includeRequestparameter to tell Jolokia to excluderequestfield from the response.
protocol in the response value contains the
protocol version used, agent is the version of
the Jolokia agent. See Jolokia protocol versions for the various
protocol versions and the interoperability. If the agent is able
to detect the server, additional meta information about this
server is returned (i.e. the product name, the vendor and
optionally some extra information added by the server detector).
Getting the basic configuration (config)
The Jolokia command config is provided to get unauthenticated information about the agent.
It duplicates (a bit) the information from the version operation, but is meant to be more universal and generic.
It serves similar purpose as /.well-known/openid-configuration endpoint for OpenID Connect providers.
POST version request
A version POST request has only a single key
type which has to be set to
config.
However, GET requests are preferred for config operation. The reason is that it’s easier to configure restricted
access in Servlet applications for GET requests (and remove the constraints for one specific URI). See Security chapter
of Java Servlet API specification.
Config response
The response value for a config request looks like this:
{
"request": {
"type": "config"
},
"value": {
"agent": "2.5.0-SNAPSHOT",
"protocol": "8.1",
"security": {
"authentication": [
{
"method": "basic",
"realm": "jolokia"
}
]
},
"id": "jolokia-7778"
},
"status": 200,
"timestamp": 1770641830
}
Jolokia only returns information for:
-
agent version
-
protocol version
-
security mechanisms used (could be
basicauthentication ormtls)
Processing parameters
Jolokia operations can be controlled by so-called processing parameters. These parameters are provided differently for POST and GET requests.
For a GET request, the processing parameters are given as normal query parameters:
<GET request URL>?param1=value1¶m2=value2&...
For example the request
http://localhost:8080/jolokia/list?maxObjects=100
will limit the response to at most 100 values.
POST requests take the processing instructions within the
JSON request under the config field:
{
"type" : "list",
"config" : {
"maxObjects" : 100
}
}
If a POST request also includes query parameters in the URL, these processing parameters are merged with the ones given within the request body. Configuration options given in the request body take precedence over the ones given as query parameters.
The list of known processing parameters is:
maxDepth-
Maximum depth of the tree traversal into a bean’s properties. The maximum value as configured in the agent’s configuration is a hard limit and cannot be exceeded by a query parameter.
maxCollectionSize-
For collections (lists, maps) this is the maximum size.
maxObjects-
Number of objects to visit in total. A hard limit can be configured in the agent’s configuration.
serializeLong-
How to serialize long numeric values in the JSON response:
numberorstring. The defaultnumbersimply serializes longs as numbers in JSON. If set tostring, 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. Available since Jolokia 2.0.3 ignoreErrors-
If set to
true, a Jolokia operation will not return an error if an JMX operation fails, but includes the exception message as value. This is useful for e.g. the read operation when requesting multiple attributes' values. Default:false includeStackTrace-
If set to
true, then in case of an error the stack trace is included. Withfalseno stack trace will be returned, and when this parameter is set toruntimeonly for RuntimeExceptions a stack trace is put into the error response. Default isfalseif not set otherwise in the global agent configuration. serializeException-
If this parameter is set to
truethen a serialized version of the exception is included in an error response. This value is put under the keyerror_valuein the response value. By default this is set tofalseexcept when the agent global configuration option is configured otherwise. canonicalNaming-
Defaults to
falseto return the defined (at creation time) format of key-values of anObjectName. If set totruethen the default sorted property list is returned. mimeType-
The MIME type to return for the response. By default, this is
text/plain, but it can be useful for some tools to change it toapplication/json. Init parameters can be used to change the default mime type. Onlytext/plainandapplication/jsonare allowed. For any other value Jolokia will fallback totext/plain. includeRequest-
A flag (defaults to
true) which controls whether the incoming request should be included in the response (under therequestkey).
Whenfalse, bulk responses have to be correlated with requests by matching the requests using index number - responses come in the same order as requests. But this may save the bandwidth for big requests.
Available since Jolokia 2.1.0 listKeys-
A flag (defaults to
false) to specify whether the response object forlist()operation should containkeysfield that lists all the keys obtained from eachjavax.management.ObjectNameof the response. This may save you time parsing the MBean name yourself.
Available since Jolokia 2.1.0 ifModifiedSince-
If this parameter is given, its value is interpreted as epoch time (seconds since 1.1.1970) and if the requested value did not change since this time, an empty response (with no
value) is returned and the response status code is set to 304 ("Not modified"). This option is currently only supported forlistrequests. The time value can be extracted from a previous' responsetimestamp. 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 to8.0) where some MBeans may point to a cached, shared MBeanInfo JSON fragment. This heavily decreases the size of thelist()response.
Available since Jolokia 2.1.0 listInterfaces-
A flag for
listoperation, which tells Jolokia to return a list of all the interfaces implemented by the MBean’s class. This is used by Jolokia JMX Connector forisInstanceOf()operation.
Available since Jolokia 2.5.0 openTypes-
A flag for
listoperation, which tells Jolokia to include information about OpenTypes used by the MBeans. This is especially useful for Jolokia implementation of JMX Connector, but also may greatly improve the way applications present the data in a visual way.
Available since Jolokia 2.5.0
Object serialization
Jolokia has some object serialization facilities in order to convert complex Java data types to JSON and vice versa. Serialization works in both ways in requests and responses and starting with Jolokia 2.5.0, the same (de)serialization mechanism is used in 3 Jolokia layers:
-
Server-side (the agent)
-
Jolokia Client which is used to access the agent in loosely typed manner (only as JSON)
-
Jolokia JMX Connector Client, which uses Jolokia Client and additional deserialization into actual Java objects
Complex data types returned from the agent can be serialized
completely into a JSON value object. Jolokia can detect cycles in
the object graph and provides a way to limit the depth of
serialization. For certain types (like
File or
ObjectName) special org.jolokia.converter.json.simplifier.SimplifierAccessor service
is used to prevent
exposing internal and redundant information.
Before Jolokia 2.5.0, object values used for setting attributes (write operation) and
passing operation parameters (exec operation) were limited to a
handful of data types. Jolokia 2.5.0 unifies the serialization mechanism.
Unified data serialization
Jolokia can serialize any object into a JSON representation when generating the response (at the agent side) or passing attributes and parameter values (at the client side). It uses some specific converters for certain well known data type with a generic bean converter as a fallback.
The following types are directly supported (recursively):
-
Arrays, lists and collections (like sets) are converted to JSON arrays
-
java.util.Mapare converted into JSON object. Note, however, that JSON Object keys are always strings (though if there is a "to string" conversion for certain data types, it is used). -
Enums are converted to their canonical name[5].
-
javax.management.openmbean.CompositeDatais converted in a JSON object, with the keys taken from theCompositeData's key set and the value are the items' values serialized accordingly. -
javax.management.openmbean.TabularDatais serialized differently depending on its internal structure. See below for a detailed explanation of this serialization mechanism including examples. -
java.lang.Classis converted to a JSON object with keysname(the class name) andinterfaces(the implemented interfaces, if any) -
org.w3c.dom.Elementis translated into a JSON object with the propertiesname,valueandhasChildNodes. -
java.net.InetAddressis serialized as a string value containing the host address -
java.io.Filebecomes a JSON object with keysname(file name),modified(date of last modification),length(file size in bytes),directory(whether the file is a directory),canonicalPath(the canonical path) andexists. -
java.lang.Moduleandjava.lang.Packageobjects are serialized simply as their names -
javax.management.ObjectNameis converted into a JSON object with the single keyobjectName. -
java.net.URLbecomes a JSON object with the keyurlcontaining the URL as String. -
java.util.Dateis represented by default in an ISO-8601 format. When used with a pathtimethe milliseconds since 1.1.1970 00:00 UTC are returned.
Since Jolokia 2.1.0 the format for date/time serialization is configurable and we also supportjava.util.Calendarandjava.time.Temporalimplementations.
Primitive and simple types (like String) are directly converted into their string presentation. All objects not covered by the list above are serialized in JSON objects, where the keys are the public bean properties of the object and the values are serialized (recursively) as described.
Serialization at the agent side can be influenced by certain processing parameters sent with the request (see Processing parameters). I.e. the recursive process of JSON serialization can be stopped when the data set gets too large. Self and other circular references are detected, too. If this happen, special values indicate the truncation of the generated JSON object.
[this]-
This label is used when a property contains a self reference
[Depth limit …. ]-
When a depth limit is used or the hard depth limit is exceeded, this label contains a string representation of the next object one level deeper. (see Processing parameters, parameter
maxDepth) [Reference …. ]-
If during the traversal an object is visited a second time, this label is used in order to break the cycle.
[Object limit exceeded]-
The total limit of object has been exceeded and hence the object are not deserialized further. (see Processing parameters, parameters
maxCollectionSizeandmaxObjects)
TabularData serialization
javax.management.openmbean.TabularType is one of the complex types used by Open MBeans specification.
TabularData serialization depends on
the type of the index used. There are three different serialization flavors of tabular types.
-
If the row type of a
TabularTypeis conforming to MXBean specification, theTabularDataobject of suchTabularTypeis serialized as a _normal map. Each row of such tabular data contains only two items -keyandvalue, which is turned into single map entry with the name fromkeyitem and the value from thevalueitem. For example:{ "mykey1" : { "f1" : "v1", "f2" : "v2", .... }, "mykey2" : { ... }, ... }Each value of such JSON has the same structure (type).
-
If the index of a
TabularTypeincludes only simple types (types with supported serialization into a String value), then Jolokia serializes theTabularDatavalues of suchTabularTypetype as nested JSON object with two simple rules:-
each index value because a key of a nested JSON object
-
the leaves of the full JSON object are full records (maps, objects) containing all the key-value pairs
Here’s an example:
+
{ "mykey1" : { "myinner1" : { "key" : "mkey1", "innerkey" : "myinner1", "item" : "value1", .... }, "myinner2" : { "key" : "mkey2", "innerkey" : "myinner2", "item" : "value2", .... }, .... }, "mykey2" : { "second1" : { "key" : "mkey3", "innerkey" : "second3", "item" : "value3", .... }, "second2" : { "key" : "mkey4", "innerkey" : "second4", "item" : "value4", .... }, .... }, .... }A Key/Value of the final map like
"key":"mkey1"provides one of the upper-level keys (here:"mkey1"). -
-
If the index includes complex data types (arrays, composite, tabular), then there’s a more direct for of
TabularDataserialization, where Jolokia returns an object with two fields:indexNamesandvalues. Index names provide information about which items of a row type constitute a key and thevaluesfield contains an array of rows. Here’s an example:{ "indexNames" : [ "key", "innerkey" ], "values" : [ { "key" : "mykey1", "innerkey" : { "name" : "a", "number" : 4711 }, "item" : "value1", .... }, { "key" : "mykey2", "innerkey" : { "name" : "b", "number" : 815 }, "item" : "value2", .... }, ... ] }As you can see,
innerKeyis a complex data, so we can’t use form #1 or #2.
Request parameter serialization
Jolokia Client is used to send:
-
MBean attribute values to set
-
MBean operation arguments to pass for the invocation
Serialization of Java objects is performed under the hood and uses the same mechanism as it’s used at the agent side.
The only difference is when GET method is used.
Since the parameters get encoded in the URL itself,
only the types which have proper "to string" converters available are supported. This means we
can’t pass maps, lists or arrays with GET requests.
This limitation with GET requests is another (besides the limitation of the URI itself where some encoding rules are required) reason that it’s much better and easier to use POST requests.
Jolokia and MXBeans
JMX specification includes special chapter about Open MBeans. Initially the JMX specification was designed to expose tha management interface (represented by a set of MBeans) while arguments and return values were supposed to use Java serialization mechanism.
However Java serialization is specific to Java platform itself and it’s not that easy to use ith with other tools and programming languages. Additionally, even if two JVM applications connect, Java serialization requires that some classes are available in both applications. Remote classloading is no longer considered a secure approach though.
That’s where Open MBeans show their benefits - remote class loading is no longer required and all the complex data structures can be represented using strict, small set of data types. There are 4 data types covered by the Open MBeans specification:
-
simple types (numbers, strings, dates, object names, booleans)
-
array types (ordered sets of other Open types)
-
composite types - key-value pairs
-
tabular types - designed on the basis of database tables, where a set of keys uniquely identify rows which in turn contain unordered sets of values of other Open Types.
What is more important from developer’s perspective is how values of the 4 data types are used to represent types like maps, lists
or Java beans.
Surprisingly, the details are available in MXBean Framework JavaDoc and not in the JMX specification itself.
For example, java.util.Map is always translated into a javax.management.openmbean.TabularData value confirming to
a javax.management.openmbean.TabularType with these characteristics:
-
row type of the tabular type is a
javax.management.openmbean.CompositeTypeusingkeyandvalueitems -
the index of the tabular type uses only the
keyitem -
keyitem uses a javax.management.openmbean.SimpleType.STRING` type
For example this Java map:
Map<String, Integer> days2026 = Map.of(
"January", 31,
"February", 28,
...
);
is represented by a tabular data with 12 rows:
-
row 1:
key=January,value=31 -
row 2:
key=February,value=28 -
…
Tracking historical values
The Jolokia agents are able to keep requested values in memory along with a timestamp. If history tracking is switched on, then the agent will put the list of historical values specific for this request into the response. History tracking is toggled by an MBean operation on a Jolokia-owned MBean (see Jolokia MBeans). This has to be done individually for each attribute or JMX operation to be tracked.
A history entry is contained in every
response for which history tracking was switched on. A certain
JMX operation on an Jolokia specific MBean has to be executed
to turn history tracking on for a specific attribute or
operation. See Jolokia MBeans for details.The
history property of the JSON response
contains an array of json objects which have two attributes:
value containing the historical value
(which can be as complex as any other value) and
timestamp indicating the time when this
value was current (as measured by the server).
JSON Response has an example of a response
containing historical values.
For multi attribute read requests, the history entry in the response is a JSON object instead of an array, where this object’s attributes are the request’s attribute names and the values are the history arrays as described above.
Proxy requests
For proxy requests, only the POST HTTP method can be used so that the given JSON request can contain an extra section for the target, which should be finally reached via this proxy request. A typical proxy request looks like
{
"type" : "read",
"mbean" : "java.lang:type=Memory",
"attribute" : "HeapMemoryUsage",
"target" : {
"url" : "service:jmx:rmi:///jndi/rmi://targethost:9999/jmxrmi",
"user" : "jolokia",
"password" : "s!cr!t"
}
}
url within the target
section is a JSR-160 service URL for the target server
reachable from within the proxy agent. user
and password are optional credentials used
for the JSR-160 Remote communication.
Agent Discovery
Jolokia agents are able to respond to certain UDP multicast requests in order to allow clients to detect automatically connection parameters. The agent URL to expose can be either manually configured for an agent or an agent can try to detect its URL automatically. This works fine for the JVM agent, for the WAR agent it only works after the first HTTP request has been processed by the agent. Due to limitations of the Servlet API the agent servlet has no clue about its own URL until this first request, which contains the request URL. Of course, the URL obtained that way can be bogus as well, since the agent may operate behind a proxy, too. So if in doubt, you should configure the agent URL manually to allow external clients to connect to the agent. The configuration options for enabling multicast requests are described in the JVM agent configuration options and Servlet init parameters agent configuration sections.
An agent which is enabled for multicast discovery will only respond to a multicast request if the Policy based security allows connections from the source IP. Otherwise a multicast request will be simply ignored. For example, if you have configured your agent to only allow request from a central monitoring host, only this host is able to detect these agents. Beside security aspects it wouldn’t make sense to expose the URL as any other host is not able to connect anyways.
Starting with version 1.2.0 the Jolokia JVM agent has this
discovery feature enabled by default which can be switched off
via --discoveryEnabled=true command line parameter or the
corresponding configuration option. For the WAR agent and OSGi
agents this feature is switched off by default since auto
detection doesn’t always work. It can be enabled with the init
parameter discoveryEnabled (in which case the auto discovery
described above is enabled) or better with discoveryAgentUrl
with the URL. Alternatively, a system property can be used
with a jolokia. prefix
(e.g. jolokia.discoveryEnabled). More on the configuration
options can be found in the agent’s configuration sections.
For sending a multicast request discovery message, an UDP
message should be send to the address 239.192.48.84, port
24884 which contains a JSON message encoded in UTF-8 with
the following format
{
"type": "query"
}
$ echo '{"type":"query"}' | socat STDIO UDP4-DATAGRAM:239.192.48.84:24884
{"agent_version":"2.5.1","agent_id":"jolokia-7778","type":"response","secured":false,"url":"http://192.168.0.165:7778/jolokia/"}{"agent_version":"2.5.1","agent_id":"jolokia-7779","type":"response","secured":false,"url":"http://192.168.0.165:7778/jolokia/"}
Any agent enabled for discovery will respond to requester on the same socket with an answer which looks like
{
"agent_version": "2.5.1",
"agent_id": "192.168.0.221-67980-7e985ce9-servlet",
"server_product": "tomcat",
"type": "response",
"server_vendor": "Apache",
"server_version": "10.1.16",
"secured": true,
"url": "http://192.168.0.221:8080/jolokia"
}
The response itself is a JSON object and is restricted to 8192
bytes maximum. The request type is either
query or response. A
query request is sent via multicast by any
interested client and each agent responds with a response of
type response. Query requests contain
only the type as property. Responses are sent back to the
address and port of the sender of the query request.
| Property | Description | Example |
|---|---|---|
|
Request type, either |
|
|
Each agent has a unique id which can be either provided during startup of the agent in form of a configuration parameter or being autodetected. If autodetected, the id has several parts: The IP, the process id, hashcode of the agent and its type. This field will be always provided. |
|
|
An optional description which can be used as a UI label if given. |
ServiceMix ESB |
|
The URL how this agent can be contacted. This URL is
typically autodetected. For the JVM agent it should be
highly accurate. For the servlet based agents, it
depends. If configured via an initialisation parameter this
URL is used. If autodetected it is taken from the first HTTP
request processed by the servlet. Hence no URL is available
until this first request was processed. This property might
be empty.
When standard |
|
|
Whether the agent was configured for authentication or not. |
|
|
The vendor of the container the agent is running in. This field is included if it could be automatically detected. |
|
|
The container product if detected |
|
|
The container’s version (if detected) |
|
Jolokia protocol versions
The protocol definition is versioned. It contains of a major
and minor version. Changes in the minor version are backward
compatible to other protocol with the same major
version. Major version changes incorporate possibly backwards
incompatible changes. This document describes the Jolokia
protocol version 8.0.
- 8.2 (since 2.5.0)
-
New
openTypesprocessing parameter forlistoperations to return information about OpenTypes used for MBean attributes and operations. Also error values are unified and contain additional.erroranderror_type_jmxfields. - 8.1 (since 2.4.0)
-
Added new
/configendpoint where some really basic information is returned (agent id and version and some security declaration (which auth methods are supported))./versionendpoint no longer returns values forusernameandpasswordproperties and returns information about used data updaters. - 8.0 (since 2.1.0)
-
We can now disable
requestfield in the response usingincludeRequestoption. There’s newlistCacheparameter that enables optimized list response. There’s also alistKeysparameter that adds"keys"field to MBeanInfo for an MBean - it contains keys obtained from MBean’sObjectName. - 7.3 (since 2.0.0)
-
Support for
notificationcommand and related JSON messages. - 7.2 (since 1.2.2)
-
Paths can now be used with wildcards (
*) which match everything in the selected level. They are especially useful with pattern read requests. - 7.1 (since 1.2.0)
-
The
versioncommand returns now the configuration global information as well with the keyconfigin the returned value. - 7.0 (since 1.1.0)
-
The
maxDepthparameter (either as processing parameter or as configuration value) is now 1 based. I.e. 0 means always "no limit" (be careful with this, though), 1 implies truncating the value on the first level for READ request. This was already true for LIST requests and the other limit values (maxCollectionSize and maxObjects) so this change is used in order to harmonize the overall behaviour with regard to limits.Enums are now serialized downstream (full support) and upstream (for type accessible to the agent).
New query parameter options
serializeException(for setting anerror_valuein case of an exception),canonicalNaming(influences how object names are returned) andincludeStackTrace(for adding or omitting stacktraces in error responses). - 6.1 (since 1.0.2)
-
Error responses contain now the original request as well, for single and bulk requests.
- 6.0 (since 1.0.0)
-
Escaping has been changed from
/-/to!/. This affects GET Urls and inner paths. - 5.0 (since 0.95)
-
javax.management.openmbean.TabularDatais serialized differently when generating the response. In fact, the serialization as an array in the former versions of this protocol is not correct, sinceTabularDatain fact is a hash and not a list. It is now generated as map (or multiple maps), depending on the declared index. Also, access via path is now an access via key, not a list index. For the special case of MXBean map serialization, where the returnedTabularDatahas a fixed format (i.e. withkeyandvaluecolumns), theTabularDatais transformed to an appropriate map.Removed JSON property
modifiedfrom the serialized JSON representation of a File return value since it duplicated thelastModifiedproperty on the same object. - 4.3 (since 0.91)
-
The
listoperation supports amaxDepthoption for truncating the answer. - 4.2 (since 0.90)
-
Response values are returned in the native JSON datatype, not always as strings as in previous versions of this protocol. Parameter serialization for writing attribute values or for arguments in exec operations has been enhanced for POST requests, which are now represented as native JSON types and not in a string representation as before. GET requests still use a simplified string representation.
- 4.0 (17.10.2010)
-
This is the initial version for Jolokia. Versions below 4 are implemented by jmx4perl.
\) can not be used, since most servlet container translate a backslash into a forward slash on the fly when given in an URL.
MBeanException, the wrapped exception’s message is used.