The JMX API is being updated by JSR 255. That JSR is currently planned to be part of Java SE 7, and some of the API changes it defines have started to appear in JDK 7. So far, the main one is a Query Language. Here's what that is and what it's for.

The JMX API has always included the idea of queries. The idea is that you can tell the queryNames method to filter the set of objects that it returns, using an object that implements the QueryExp interface. For example, you can pick out only those MBeans that have an attribute calledEnabled with the value true and an attribute called Owner with the value"Duke".

The recommended way to obtain QueryExp objects is using the static methods of the Query class. That way, your QueryExpconsists only of standard classes that must be present on all JMX implementations and there are no worries about classes that are present on the client but not the server.

Up until now, the way to code the query I described above was this:

 QueryExp query =
     Query.and(Query.eq(Query.attr("Enabled"), Query.value(true)),
               Query.eq(Query.attr("Owner"), Query.value("Duke")));

While it's possible to decipher that and determine that it does indeed mean what I described, it isn't very easy. The idea of the query language is that you can get the sameQueryExp object like this:

 QueryExp query = Query.fromString("Enabled = true and Owner = 'Duke'");

Much easier to understand! (Let me stress that this is just an alternative way of writing existing queries. It doesn't introduce any new types of query.)

The query language is closely based on the WHEREclause of SQL SELECT queries. Here are the other examples from the specification:

Message = 'OK'
Selects MBeans that have a Message attribute whose value is the string OK.
FreeSpacePercent < 10
Selects MBeans that have a FreeSpacePercentattribute whose value is a number less than 10.
FreeSpacePercent < 10 and WarningSent = false
Selects the same MBeans as the previous example, but they must also have a boolean attribute WarningSent whose value is false.
SpaceUsed > TotalSpace * (2.0 / 3.0)
Selects MBeans that have SpaceUsed andTotalSpace attributes where the first is more than two-thirds the second.
not (FreeSpacePercent between 10 and 90)
Selects MBeans that have a FreeSpacePercentattribute whose value is not between 10 and 90, inclusive.
FreeSpacePercent not between 10 and 90
Another way of writing the previous query.
Selects MBeans that have a Status attribute whose value is one of those three strings.
Message like 'OK: %'
Selects MBeans that have a Message attribute whose value is a string beginning with "OK: ". Notice that the wildcard characters are SQL's ones. In the query language, % means "any sequence of characters" and_ means "any single character". In the rest of the JMX API, these correspond to * and %respectively.
instanceof ''
Selects MBeans that are instances of, as reported by MBeanServer.isInstanceOf.
like 'mydomain:*'
Selects MBeans whose ObjectNames have the domainmydomain.

If you're familiar with SQL, all of these should be familiar, except the last two, which have no SQL equivalent.

The full specification also includes a formal grammar, which I won't reproduce here. I'll just say that I got to liberate my repressed inner compiler geek when writing the parser.

Other uses for the Query Language

Apart from making it easier to write code that does queries, a standard query language is very practical for tools like JConsole or VisualVM that might want to allow the user to select a subset of MBeans using a query. A simple text field can now be used to do this.

The query language also provides one solution to a problem with the existing Query API. The methods of the Query class allow you to construct objects that implementQueryExp. But if you have such an object, there is no easy way to look inside to see what kind of query it is. As well asQuery.fromString, the new API includes Query.toString to do the reverse transformation. With these two methods you can for example save a query in a text file, or send it over a text-based protocol like SOAP, and reconstruct it later.

(You might be wondering why we bothered definingQuery.toString. Couldn't we just have said that the standard toString() method would do the right thing? The main reason it can't is that ObjectName is a QueryExp, but the syntax to include an ObjectName in a query is for example "like '*:type=Foo,*'", whileObjectName.toString() will be just "*:type=Foo,*".)

Custom queries

Nothing prevents you from writing your own class that implementsQueryExp and giving it to queryNames. Although custom queries are powerful, they're also discouraged. Most queries that you want can be composed out of the standard set. The problem with custom implementations is that, if you want to do your query remotely, you need to arrange for the implementation class to be present on both client and server.

This is why Query.toString andQuery.fromString don't specify any handling for non-standard queries. Although various possibilities could be imagined for what that might look like, people could be seduced into using custom queries without realizing the deployment headaches that that can lead to down the road.

Why SQL?

You might be wondering why the query language is based on SQL, which is a database query language. Is that really appropriate to query management objects?

Although it's far from obvious, the original JMX query API was closely based on SQL too. In fact, the places where the query language described here differs from SQL are essentially the places where the JMX query API has changed since its original version. One strong historical hint here is that the Reference Implementation has always used SQL syntax in the toString() methods of the various QueryExp classes, even going as far as to replace * and ? with their SQL equivalents % and _.

Quite apart from this, SQL is familiar to very many programmers, and is also the inspiration for the query languages used by the Java Persistence API (JPA) and the Java Message Service (JMS).

Evolution of the standard queries

The set of available standard queries has expanded slightly over time. The original set was defined in version 1.0 of the JMX API, way back in 2000.

In version 1.2 of the API, we made ObjectName implement QueryExp, which gave users a way to match ObjectName patterns themselves, and also meant that you could usequeryNames to find MBeans that match a patternAND another pattern, and various other Boolean combinations. This is the last available standalone version, and the version that was included with Java SE 5.0.

In Java SE 6 we added Query.isInstanceOf.

In Java SE 7 (assuming plausibly that that includes JSR 255, the new JMX API), in addition to the query language, we're including the ability to use dotted attribute expression likeA.b.c, with the same meaning as for monitors. So for example you could find out which memory pools still have init the same ascommitted in their MemoryUsage using code like this:

MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName poolPattern = new ObjectName("java.lang:type=MemoryPool,*");
QueryExp q = Query.fromString("Usage.init = Usage.committed");
// or: Query.eq(Query.attr("Usage.init"), Query.attr("Usage.committed"));
Set<ObjectName> names = mbs.queryNames(poolPattern, q);

If you're sending either isInstanceOf or dotted attribute expressions over a network connection, you need to have some way of knowing that the other end supports those. Otherwise, you have to avoid using either of these.

(We could add a method, say Query.isSupported(QueryExp, MBeanServerConnection) that tells you whether the given MBean Server supports the given query, by examining its specification version. But most of the time, either you wouldn't know what to do if it returned false; or you would know what to do, and you could just do that always.)

A pattern matching problem

One area where I'm not sure the query language does the right thing is with patterns. In the existing API, if you want to query for all MBeans that have a State attribute that is a string in parentheses, you would use
Query.match(Query.attr("State"), Query.value("(*)")).
In the Query Language, you can instead write
Query.fromString("State like '(%)'").
As before, that's much easier to read, but what's the story with* versus %?

Query.match uses the well-known "shell-style" wildcards, where ? matches any single character and* matches zero or more characters. On the other hand, the LIKE operator in the SQL standard uses the characters _ and %for the same thing. Both the Java Persistence Query Language from Java EE and JMS MessageSelectors have a LIKE operator that uses the SQL characters. So people familiar with these will expect theLIKE operator in the JMX query API to work the same way. On the other hand, people who are familiar withQuery.match or shell wildcards orObjectName wildcards will expect the other convention. It's particularly messy when you compare a query that matchesObjectNames, corresponding to ObjectName.apply, with one that matches strings:

QueryExp objectNameQuery = new ObjectName("mydomain:*");
// prints: LIKE 'mydomain:*'
// this is not a standard SQL query so we don't have to respect precedent

QueryExp objectNameStringQuery =
    Query.match(Query.attr("Name.canonicalName"), Query.value("mydomain:*"));
// prints: Name.canonicalName like 'mydomain:%'

Never mind the inconsistent case of LIKE (which I just noticed). Can we really live with * in some places and % in others?

This is just the beginning

You can expect other new features from 2.0 to show up in the JDK 7 snapshots in the coming months. Namespaces, Event Service, localization support, you name it!

[Tags: jmx sql jdk.]