A query language for the JMX API
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
Messageattribute whose
value is the stringOK. 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 attributeWarningSentwhose value
is false. SpaceUsed > TotalSpace * (2.0 / 3.0)- Selects MBeans that have
SpaceUsedand
TotalSpaceattributes 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
Statusattribute whose
value is one of those three strings. Message like 'OK: %'- Selects MBeans that have a
Messageattribute 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">
ObjectNames 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("<b style="color: red">Usage.init = Usage.committed</b>");
// 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 givenMBean 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
<a
href="http://download.java.net/jdk7/docs/api/javax/management/Query.html#match(javax.management.AttributeValueExp,%20javax.management.StringValueExp)">Query.match</a>(Query.attr("State"),
Query.value("<b style="color: red">(*)</b>")).In
the Query Language, you can instead write
Query.fromString("State like '<b style="color:
red">(%)</b>'").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 otherhand, the
LIKE operator in the
href="http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt">SQLstandard uses the characters
_ and% for the same thing. Both the
href="http://java.sun.com/javaee/5/docs/tutorial/doc/bnbuf.html#bnbvg">JavaPersistence 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 SQLcharacters. So people familiar with these will expect the
LIKE operator in the JMX query API to work the sameway. On the other hand, people who are familiar with
Query.match or shell wildcards orObjectName wildcards will expect the otherconvention. 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!
- Login or register to post comments
- Printer-friendly version
- emcmanus's blog
- 4836 reads






Comments
by fbogdan - 2009-05-30 05:50
Hello, how do you query for the J2EEServer ? I've tried something like: mbeanServerConn.queryMBeans(null, "*:j2eeType=J2EEServer") but no success ?