Skip to main content

A query language for the JMX API

Posted by emcmanus on April 25, 2008 at 8:21 AM PDT

The JMX API is being updated by "http://jcp.org/en/jsr/detail?id=255">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 "http://openjdk.java.net/projects/jdk7/">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 "http://download.java.net/jdk7/docs/api/javax/management/MBeanServerConnection.html#queryNames(javax.management.ObjectName,%20javax.management.QueryExp)">
queryNames
method to filter the set of objects
that it returns, using an object that implements the "http://download.java.net/jdk7/docs/api/javax/management/QueryExp.html">
QueryExp
interface. For example, you can pick out
only those MBeans that have an attribute called
Enabled 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 "http://download.java.net/jdk7/docs/api/javax/management/Query.html">
Query
class. That way, your QueryExp
consists 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 same
QueryExp 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 WHERE
clause 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 FreeSpacePercent
attribute 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 and
TotalSpace attributes where the first is more than
two-thirds the second.
not (FreeSpacePercent between 10 and 90)
Selects MBeans that have a FreeSpacePercent
attribute whose value is not between 10 and 90, inclusive.
FreeSpacePercent not between 10 and 90
Another way of writing the previous query.
Status in ('STOPPED', 'STARTING', 'STARTED')
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
  'javax.management.NotificationBroadcaster'
Selects MBeans that are instances of "http://download.java.net/jdk7/docs/api/javax/management/NotificationBroadcaster.html">
javax.management.NotificationBroadcaster
, as
reported by "http://download.java.net/jdk7/docs/api/javax/management/MBeanServer.html#isInstanceOf(javax.management.ObjectName,%20java.lang.String)">
MBeanServer.isInstanceOf
.
like 'mydomain:*'
Selects MBeans whose "http://download.java.net/jdk7/docs/api/javax/management/ObjectName.html">
ObjectName
s have the domain
mydomain.

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 "http://download.java.net/jdk7/docs/api/javax/management/Query.html#formal-ql">
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 "http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/tip/src/share/classes/javax/management/QueryParser.java">
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 "http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html">
JConsole
or "https://visualvm.dev.java.net/">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 implement
QueryExp. 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 as Query.fromString, the new API includes href=
"http://download.java.net/jdk7/docs/api/javax/management/Query.html#toString(javax.management.QueryExp)">
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 defining
Query.toString. Couldn't we just have said that the
standard href="http://download.java.net/jdk7/docs/api/java/lang/Object.html#toString()">toString()
method would do the right thing? The main reason it can't is that
href="http://download.java.net/jdk7/docs/api/javax/management/ObjectName.html">ObjectName
is a QueryExp, but the syntax to include an
ObjectName in a query is for example "

like
'*:type=Foo,*'
", while ObjectName.toString()
will be just "*:type=Foo,*".)

Custom queries

Nothing prevents you from writing your own class that
implements QueryExp and giving it to href="http://download.java.net/jdk7/docs/api/javax/management/MBeanServer.html#queryNames(javax.management.ObjectName,%20javax.management.QueryExp)">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 and
Query.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 href="http://download.java.net/jdk7/docs/api/javax/management/ObjectName.html">ObjectName
implement QueryExp, which gave users a way to match
ObjectName patterns themselves, and also meant that
you could use queryNames to find MBeans that match
a pattern AND 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 href="http://download.java.net/jdk7/docs/api/javax/management/Query.html#isInstanceOf(javax.management.StringValueExp)">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 like
A.b.c, with the same meaning as for href="http://java.sun.com/javase/6/docs/api/javax/management/monitor/package-summary.html#package_description">monitors.
So for example you could find out which href="http://java.sun.com/javase/6/docs/api/java/lang/management/MemoryPoolMXBean.html">memory
pools still have init the same as
committed in their href="http://java.sun.com/javase/6/docs/api/java/lang/management/MemoryUsage.html">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 href="http://download.java.net/jdk7/docs/api/javax/management/MBeanServerDelegateMBean.html#getSpecificationVersion()">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

	  href="http://download.java.net/jdk7/docs/api/javax/management/Query.html#match(javax.management.AttributeValueExp,%20javax.management.StringValueExp)">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 href="http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt">SQL
standard uses the characters _ and
% for the same thing. Both the href="http://java.sun.com/javaee/5/docs/tutorial/doc/bnbuf.html#bnbvg">Java
Persistence Query Language from Java EE and JMS href="http://java.sun.com/javaee/5/docs/api/javax/jms/Message.html">Message
Selectors have a LIKE operator that uses the SQL
characters. So people familiar with these will expect the
LIKE operator in the JMX query API to work the same
way. On the other hand, people who are familiar with
Query.match or shell wildcards or
ObjectName wildcards will expect the other
convention. It's particularly messy when you compare a query
that matches ObjectNames, corresponding to href="http://download.java.net/jdk7/docs/api/javax/management/ObjectName.html#apply(javax.management.ObjectName)">ObjectName.apply,
with one that matches strings:

QueryExp objectNameQuery = new ObjectName("mydomain:*");
System.out.println(Query.toString(objectNameQuery));
// 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:*"));
System.out.println(Query.toString(objectNameStringQuery));
// 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 href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5072268">other
new features from 2.0 to show up in the href="https://jdk7.dev.java.net/">JDK 7 snapshots in the
coming months. Namespaces, Event Service, localization
support, you name it!

[Tags: jdk.]

Related Topics >>

Comments

Hello, how do you query for the J2EEServer ? I've tried something like: mbeanServerConn.queryMBeans(null, "*:j2eeType=J2EEServer") but no success ?