blob: 4fd2ae6516578c22a884fc24024ee21e43f62ed7 [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<!--
Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Oracle nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<title>JMX(TM) "scandir" Example</title>
</head>
<body>
<h1><center>Java<font size="-1"><sup>TM</sup></font> Management Extensions (JMX<font size="-1"><sup>TM</sup></font>) <i>scandir</i> Example</center></h1>
<h2><a name="h2-Introduction">Introduction</a></h2>
<ul>
<p>The JMX <i>scandir</i> example is an application that
scans parts of a filesystem - e.g. a set of directories
used by a number of lab machines when running tests - in
order to clean up and optimize disk space by removing
obsolete files - e.g. files that are leaked by the test
suites running on those machines, like coredump files, or
temporary files that might remain after a test crash.
It could also serve as a basis for an application that
would monitor disk usage and suggest removal of old big
long-unaccessed files.
</p>
<p>The JMX <i>scandir</i> example does not however implement
the full fledged logic that such an application might
have. It implements a subset of this logic which is
sufficient to demonstrate common patterns and
solutions used when implementing a monitoring and
management interface for an application with JMX
Technology.</p>
<p>This example is an advanced JMX example, which presents
advanced JMX concepts. It is assumed that the reader is already
familiar with the JMX API. Newcomers to JMX Technology are
invited to have a look at the <a
href="http://java.sun.com/javase/6/docs/technotes/guides/jmx/"
>JMX API Overview, Tutorial and Examples</a> before going any further.
</p>
<p></p>
<hr>
<blockquote>
<u>Note:</u> This example was developed using <a
href="http://www.netbeans.org">NetBeans 5.0 IDE</a>. The instructions
given in this document to build, run, and test the example assume that
you have at your disposal:
<ul><li>either <a href="http://www.netbeans.org">NetBeans 5.0 IDE</a>,</li>
<li>or <a href="http://ant.apache.org/">Apache Ant 1.6.5</a> and
<a href="http://sourceforge.net/projects/junit/">JUnit 3.8.1 or
3.8.2</a><br>
(JUnit is only needed to run the example's unit tests).
</li>
</ul>
<p><a name="setup">In order to build the example</a>,
<u>you may need to copy the jmx-scandir</u>
directory to somewhere where you have write permissions.
<br>In that case, you will need to update the <i>nbjdk.home</i> variable
in the copied <i><a href="build.properties">build.properties</a></i>
file located at the root of the copied project directory.
Please make sure that this variable points to the JDK 6 home directory.
</p>
<p>If you wish to run the testsuite from within the <a
href="http://www.netbeans.org">NetBeans IDE</a> you will also have
to set the <i>libs.junit.classpath</i> variable in
<a href="build.properties">build.properties</a>.
The <i>libs.junit.classpath</i> variable should point to your
<a href="http://sourceforge.net/projects/junit/">junit.jar</a>,
version 3.8.1 or 3.8.2.
</p>
</blockquote>
<hr>
<p></p>
<p><u>Table Of Contents:</u></p>
<p><center>[<a href="#h2-Generating">Generating&nbsp;the&nbsp;Java&nbsp;Documentation</a>]
[<a href="#h2-Overview">Overview&nbsp;of&nbsp;the&nbsp;<i>scandir</i>&nbsp;Example</a>]
[<a href="#h2-API-Doc">API&nbsp;Documentation&nbsp;and&nbsp;Sources</a>]
[<a href="#h2-Patterns">Patterns,&nbsp;Best&nbsp;Practices,&nbsp;and&nbsp;Common&nbsp;Pitfalls</a>]
[<a href="#h2-Testing">Testing&nbsp;the&nbsp;<i>scandir</i>&nbsp;Example</a>]
[<a href="#h2-Running">Running&nbsp;the&nbsp;<i>scandir</i>&nbsp;Example</a>]
[<a href="#h2-Playing">Playing&nbsp;with&nbsp;JConsole</a>]
[<a href="#h2-Turning">Turning&nbsp;the&nbsp;example&nbsp;into&nbsp;a&nbsp;Secure&nbsp;JMX&nbsp;Application</a>]
[<a href="#h2-Connecting">Connecting&nbsp;to&nbsp;the&nbsp;Secure&nbsp;JMX&nbsp;Application</a>]
[<a href="#h2-Conclusion">Conclusion</a>]
[<a href="#h2-References">References</a>]</center></p>
</ul>
<h2><a name="h2-Generating">Generating the Java Documentation</a></h2>
<ul>
<p>Before reading further, you will need to generate the
Java Documentation for the example's sources.</p>
<p>In the example root directory (where the <code>build.xml</code>
file is located) run the following command:
<pre>ant javadoc</pre>
</p>
<p>Alternatively you can open the jmx-scandir project with the
NetBeans IDE and generate the Javadoc from its <code>Build</code>
menu.
</p>
<p>If building the documentation fails, please make sure to read the
<a href="#setup">note</a> at the beginning of this document.</p>
</ul>
<h2><a name="h2-Overview">Overview of the <i>scandir</i> Example</a></h2>
<ul>
<p>The JMX <i>scandir</i> example is built around the
following MBeans:</p>
<ul>
<li>The first MBean we will present here is the
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
title="A DirectoryScannerMXBean looks for file matching a given set of criteria, starting at a given root."
>DirectoryScannerMXBean</a>. <br>A
<code>DirectoryScannerMXBean</code> is an MBean that scans a
file system starting at a given root directory, and then looks
for files that match the given criteria. When such a file is
found, the <code>DirectoryScannerMXBean</code> takes the
action for which it was configured: emit a notification,
<i>and/or</i> log a <code>record</code> for this file,
<i>and/or</i> delete that file. The code that would actually
delete the file is commented out - so that nothing valuable is
lost if the example is run by mistake on the wrong set of
directories.<br> <code>DirectoryScannerMXBeans</code> are
created by the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> - see next item on the list, from its
configuration.
</li>
<li>
The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> is the actual entry point of the
application. It reads the application's
configuration, and from that configuration,
will create a <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManager.html"
title="The ResultLogManager is in charge of managing result logs"
>ResultLogManager</a> and some <a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
title="A DirectoryScannerMXBean looks for file matching a given set of criteria, starting at a given root."
>DirectoryScannerMXBeans</a>.
<br>The <code>ScanManagerMXBean</code> lets you start, stop, and
schedule directory scans. The
<code>ScanManagerMXBean</code> is a singleton
MBean: there can be at most one instance of such
an MBean registered in a given MBeanServer.
</li>
<li>The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a> is an MBean which is able to
load/save the configuration to/from an XML file. It
will also let you modify that configuration - by e.g.
creating new directory scanners in there.
The corresponding MBeans will be created later, only
when you later
ask the <code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> </code> to apply the
configuration again.<br>
The <code>ScanDirConfigMXBean</code> is created by the
<code>ScanManagerMXBean</code>, when the
<code>ScanManagerMXBean</code> is registered.
It is also possible to create an alternate
<code>ScanDirConfigMXBean</code>, and to switch the
<code>ScanDirConfigMXBean</code> to use one or the other
configuration.
<br>An example of XML configuration file is given
<a href="src/etc/testconfig.xml"
title="An Example Of Configuration"
>here</a>. Although you could edit such a file by
hand, it is easier to do it programmatically (or
with <a href="#JConsole">JConsole</a>) through
the <code>ScanDirConfigMXBean</code> interface.
</li>
<li>The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManagerMXBean.html"
title="The ResultLogManagerMXBean is in charge of managing result logs"
>ResultLogManagerMXBean</a> is in charge of managing result logs.
<br>Directory Scanners can be configured to log a
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/config/ResultRecord.html"
title="A ResultRecord contains information about a file matching the criteria of a Directory Scanner"
>ResultRecord</a> whenever they take action upon a file that
matches their criteria. The <code>ResultLogManagerMXBean</code> is
responsible for logging these result records.
The <code>ResultLogManagerMXBean</code> can be configured to log
such records to a flat file, or into a log held in memory, or
both. Both logs (file and memory) can be configured with a
maximum capacity.
<br>When the maximum capacity of the memory
log is reached, its first entry (i.e. its oldest entry) is
removed to make place for the latest one.
<br>When the maximum
capacity of the file log is reached, the file is
renamed by appending a tilde '~' to its name and a
new result log is created.
<br>The <code>ResultLogManagerMXBean</code>
will let you interactively clear these result logs, change their
capacity, and decide where (memory or file) to log.
The memory log is useful in that its content can be interactively
returned by the <code>ResultLogManagerMXBean</code>, while
the file log doesn't have this facility.<br>
The result logs are intended to be used by e.g. an offline
program that would take some actions on the files that
matched the scan criteria:
<br>The <i>scandir</i> application
could be configured to only produce logs (i.e. takes no
action but logging the matching files), and the real
action could be performed by another program or module (e.g. mail the result log to the engineer who
maintains the lab, or parse that log and delete all the
files listed there, or parse the log and prepare and send
a single mail to each owner of matching files, containing
the list of files they should consider deleting).<br>
The <code>ResultLogManagerMXBean</code> is a singleton
MBean created by the <code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> </code>
which reads and writes its configuration from the
<code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a></code>.
</li>
</ul>
<p>An application <code>main()</code> method is
provided in the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirAgent.html"
>ScanDirAgent</a> class. The <code>main()</code> simply registers
a <code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> </code> in the platform MBeanServer, and
then waits for someone to call <code>close()</code> on the
<code>ScanManagerMXBean</code>.
</p>
<p>When the <code>ScanManagerMXBean</code> is registered, it
will create a default <code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a></code> bound
to a default XML config file.
</p>
<p>The application's default XML config file is determined as
follows:
<ol>
<li>If the property <code>scandir.config.file</code> is
defined, the default application file will be the
file pointed to by this property. If that file
doesn't exist, it will be created when
<code>ScanDirConfigMXBean.save()</code> is
invoked.
</li>
<li>Otherwise the application config file is
assumed to be a file called <code>jmx-scandir.xml</code>,
located in the user's directory (as defined by
the System property <code>user.home</code>).
If that file doesn't exists, it will be created when
<code>ScanDirConfigMXBean.save()</code> is
invoked.
</li>
</ol>
<p>It is worth noting that this project is defined to
run with the following properties:
<pre>-Djava.util.logging.config.file=logging.properties</pre>
<pre>-Dscandir.config.file=src/etc/testconfig.xml</pre>
With <code>ScanDirAgent</code> defined as the project's
main class. Hence when you invoke from the NetBeans IDE
<i>Run Project</i> on the <i>jmx-scandir</i> project,
or <i>Run file</i> on the <code>ScanDirAgent</code>, the
application starts with the test configuration provided in
<a href="src/etc/testconfig.xml"
title="An Example Of Configuration"
>src/etc/testconfig.xml</a>
</p>
</ul>
<h2><a name="h2-API-Doc">API Documentation and Sources</a></h2>
<ul>
<p>Once generated, the Javadoc of example classes can
be found starting from <a href="dist/javadoc/index.html"
title="The API Documentation"
><code>dist/javadoc/index.html</code></a>.</p>
<p>You can view the sources in the <a
href="src"
title="The Example Source Tree"
><code>src</code></a> subdirectory.</p>
</ul>
<h2><a name="h2-Patterns">Patterns, Best Practices, and Common Pitfalls</a></h2>
<ul>
<p>This section discusses some common patterns and
design choices that this example demonstrates, and some pitfalls that
it avoids.
</ul>
<h3>MBeans or MXBeans?</h3>
<ul>
<p>What is an MXBean? MXBeans made their appearance in
J2SE 5.0 (Tiger), with the Management and Monitoring
API of the JVM. However, Java SE 6 is the first
Java SE release that contains a standard framework which
makes it possible to create and register your own MXBeans.
</p>
<p>MXBeans are a special kind of MBean, which once registered
in the MBeanServer, get automatically transformed into
OpenMBeans. From a developer point of view, nothing changes:
A Wombat MBean can become an MXBean simply by renaming
its <code>WombatMBean</code> interface into <code>WombatMXBean</code>.</p>
<p>Using MXBeans rather than plain Standard MBean brings its
own advantages:</p>
<ul>
<li>
Generic tools, like JConsole, will be able to
display and interact with your MXBeans nicely, even
if your MXBean interfaces reference custom types
- e.g. custom Java enums. This is because all the types
exposed by your MXBeans are converted to Open Types.
Just look at the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a> with JConsole and you will
understand the benefits.
</li>
<li>
When writing a programmatic client, you can obtain
a proxy that implements the original MXBean interface,
and forget about the Open Type conversion.
The JUnit unit tests that come with this example
use this feature very widely. Have a look at them.
</li>
<li>
The MXBean framework also lets you nicely navigate
from one MXBean to another: your MXBeans can
have attributes and parameters which are proxies
to other MXBeans! We demonstrate this in the
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> which exposes a list
of <code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
title="A DirectoryScannerMXBean looks for file matching a given set of criteria, starting at a given root."
>DirectoryScannerMXBean</a></code> and points
towards a <code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a></code>.
</li>
</ul>
<p>In short, MXBeans are so much easier to use that
this example doesn't even have a single regular
Standard MBean.
</p>
<p>See also <a
href="http://weblogs.java.net/blog/emcmanus/archive/2006/02/what_is_an_mxbe.html"
title="What is an MXBean?"
>What is an MXBean?</a>
and <a
href="http://weblogs.java.net/blog/emcmanus/archive/2006/06/intermxbean_ref.html"
title="Inter-MXBean references"
>Inter-MXBean References</a>.
</p>
<blockquote><u>Hint:</u> In order to simplify the task of coding a
JMX programmatic client, we recommend that getters, setters, and
operations defined in MBean and MXBean interfaces throw
<code>IOException</code>. Proxy objects will then be able
to rethrow directly any <code>IOException</code> received from
their underlying MBean Server connection, without wrapping
them into <code>UndeclaredThrowableExceptions</code>.<br>
Since the life cycle of the proxy object is not directly tied to
the life cycle of the MBean it proxies, you may also want to
have all methods in the MBean or MXBean interface throw
<code>InstanceNotFoundException</code> or more generally
<code>JMException</code>.
</blockquote>
</ul>
<h3>MBean Names - aka ObjectNames</h3>
<ul>
<p>As you must know if you've been studying JMX, MBeans are
named objects. The names of MBeans are represented by
instances of <code>ObjectName</code>. An ObjectName is
composed of a <i>domain</i>, followed by a colon ':',
followed by a comma-separated list of <i>key=value</i>
pairs.<br>
The ordering of the <i>key=value</i> pairs is not
important, but <code>ObjectNames</code> are case sensitive
(both keys and values are case sensitive) and <b>white space
is not ignored</b>.<br>
A common pitfall for JMX beginners is to inadvertently
insert white space after commas into an ObjectName,
and expect that two ObjectNames which differ only by such white
space will be considered identical. This is not the
case.<br>
As an example, the ObjectName '<b><code>D:k1=v1, k2=v2, k3=v3</code></b>' has
three keys, which are '<b><code>k1</code></b>', '<b><code> k2</code></b>',
and '<b><code> k3</code></b>': beware
of the space in the name of the second and third
keys!<br>
It is therefore a different ObjectName from
'<b><code>D:k1=v1,k2=v2,k3=v3</code></b>' (the keys are now
'<b><code>k1</code></b>', '<b><code>k2</code></b>', and
'<b><code>k3</code></b>'), but the same ObjectName as
'<b><code>D: k2=v2, k3=v3,k1=v1</code></b>', and yet different
from '<b><code>D:k2=v2, k3=v3, k1=v1</code></b>'!
<p>In this example, we are following the rules
for ObjectName suggested in the <a
href="http://java.sun.com/products/JavaManagement/best-practices.html"
>JMX Best Practices</a>:</p>
<ul>
<li>ObjectNames should be <a
href="http://java.sun.com/products/JavaManagement/best-practices.html#mozTocId654884"
>predictable</a>
</li>
<li>The domain part of our ObjectNames starts with a Java
package name
</li>
<li>Our ObjectNames contain a <code>type=</code>
key property. This property is different for every
object type in our domain.
</li>
<li>For every ObjectName with a given type, we have the same set of key
properties with the same syntax and semantics for their values - in
fact we only use an additional <code>name=</code> key.
</li>
<li>When there can only be one instance of a given type
there aren't any other key properties than <code>type=</code>.
The ObjectNames of the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> and <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManagerMXBean.html"
title="The ResultLogManagerMXBean is in charge of managing result logs"
>ResultLogManagerMXBean</a>, which are both singleton MBeans, are
composed in this way.
</li>
<li>When there can be several instances of a given type,
we differentiate them by further key properties.
To achieve this, we are using the most usual key property
in addition to <code>type=</code>: the <code>name=</code> key.
In this example, a key property list of the form
<code>type=X,name=Y</code> is always enough to uniquely name
an MBean. Tools like jconsole are usually aware
of the semantics of the <code>type=</code> key and
<code>name=</code> key, and are therefore able to
display this form of name in a way that
is easier to read than other name forms.
</li>
</ul>
<p>The rules listed above are implemented by a couple
of static helper functions in the <a
href="src/com/sun/jmx/examples/scandir/ScanManager.java"
title="ScanManager.java"
>ScanManager</a> class. See the code of the
<b><code>makeSingletonName</code></b> and
<b><code>makeMBeanName</code></b> methods.
</p>
</ul>
<h3>Inter MBean Navigation</h3>
<ul>
<p>One of the most common problems that needs to be solved
when designing a management interface with JMX is to
choose a representation for inter-MBean relationships.<br>
Prior to Java 6, there were basically three possible
choices:</p>
<ul>
<li><b>Make the relation appear in the ObjectName</b>.
For instance, if MBean B was contained in
MBean A, you could choose to name MBean B so
that its parent relationship with MBean A
appeared in its name. <br>
The obvious limitation of this solution is that
it only allows to model one such relation (an
MBean has only one name) and the relation must
be fixed - it cannot change during the life of
the MBean since the name of an MBean cannot
change.<br>
This scheme is therefore mostly used when
the application MBeans are modeling objects
which are conceptually contained within
each other in a tree-like structure.
<br>For instance, most MBean names defined by
<a href="http://jcp.org/en/jsr/detail?id=77"
>J2EE Management (JSR 77)</a> follow
this scheme.
</li>
<li><b>Design getters and setters (or operations) which
return <code>ObjectName</code> or
<code>ObjectName[]</code> values</b>. The ObjectNames
point to the MBeans which are related to that
object. For instance , <a
href="http://glassfish.dev.java.net/"
title="Open Source Java EE 5 Application Server"
>GlassFish</a>
defines MBeans which also use this pattern.
</li>
<li><b>Use the JMX RelationService</b>. The JMX RelationService
is quite powerful, but simple relationships often
do not justify that overhead.
</li>
</ul>
<p>In Java 6, these three possibilities still remain, but
the new MXBean framework brings up an interesting
alternative. Instead of returning an ObjectName or
an ObjectName array, <b>an MXBean can return a proxy</b>
to its related MXBeans. This is how we have chosen to
implement our inter MBean relationships in this
example:
<br>For instance the
<code>ScanManagerMXBean</code>/<code>DirectoryScannerMXBean</code>
relationship and the
<code>ScanManagerMXBean</code>/<code>ScanDirConfigMXBean</code>
relationships are implemented in this way.
<p>
The additional benefit, as compared to returning ObjectNames or
using the RelationService is that interface type of the MBeans
which are pointed to by the relationship becomes directly
apparent. The method:
<pre>
public Map&lt;String,DirectoryScannerMXBean&gt; getDirectoryScanners();
</pre>
makes it immediately obvious that the MBeans to which we point are
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
title="A DirectoryScannerMXBean looks for file matching a given set of criteria, starting at a given root."
>DirectoryScannerMXBeans</a>. It would have been much less obvious in prior
versions of Java SE, were the returned type would have had to be
<code>Map&lt;String,ObjectName&gt;</code>, or
even worse just <code>Map</code>.
</p>
<p>However, it must be clear that the behaviour will be
quite different when an MXBean is returned as compared
to when a simple bean is returned.
</p>
<p>When an MXBean is returned, the remote client sees either
an ObjectName, if it is a generic client like jconsole, or
a proxy to a remote MXBean, if the client is working with the
MXBean interface. Invoking an operation on one of the
proxy returned by a method such as
<code>getDirectoryScanners</code> will cause the
MBean to be invoked on the remote server side.
</p>
<p>If <code>getDirectoryScanners</code> were
defined as:
<pre>
public Map&lt;String,DirectoryScannerConfig&gt; getDirectoryScanners();
</pre>
then invoking a method on one of the returned objects
would have absolutely no effect on the remote
server side - because the returned objects in this
case would simply be a bunch of serialized data objects.
</p>
<p>It is worth noting that although an MXBean interface
can have getters and operations which return an MXBean
interface, a regular standard MBean shouldn't have
any getters or methods which return MBean interfaces or
MXBean interfaces.
</p>
<p>For more information see also <a
href="http://weblogs.java.net/blog/emcmanus/archive/2006/06/intermxbean_ref.html"
title="Inter-MXBean references"
>Inter-MXBean References</a>.
</p>
</ul>
<h3>The MBeanRegistration interface, or how an MBean can
know or provide its own name</h3>
<ul>
<p>
Sometimes, an MBean needs to have a reference to the
MBeanServer in which it is registered, or needs to know
with which ObjectName it has been registered.
</p>
<p>
Sometimes also, an MBean may need to perform some
checks before being registered, or will need
to carry out some actions right after it has been
successfully registered in the MBeanServer.
</p>
<p>
Sometimes again, an MBean may need to perform some
checks, or some cleaning actions, just before, or
just after, it is unregistered.
</p>
<p>
When an MBean has such needs, the easiest solution
for it is to implement the <code>MBeanRegistration</code>
interface.
</p>
<p>The <code>MBeanRegistration</code> interface is a callback
interface which defines pre and post registration and
unregistration callbacks.
</p>
<p>
When an MBean implementing this interface is created
(with <code>createMBean</code>) or registered
(with <code>registerMBean</code>) in an MBeanServer,
the MBeanServer will call the <code>preRegister</code>
and <code>postRegister</code> method implemented by
the MBean. The <code>preRegister</code> method
has an <code>MBeanServer</code> and <code>ObjectName</code>
parameter, which are passed by the MBeanServer to the
MBean. The MBean can store the reference it is being passed
in a private instance variable for later use.
</p>
<p>
Most of the MXBeans we have defined in this example
implement the <code>MBeanRegistration</code> interface. The table
below show how our MBeans use this interface to control
their own names, make sanity checks, perform
initialization steps or cleanup actions.
</p>
<p><br><center>
<table border="1" cellpadding="4" cellspacing="2"
bgcolor="#eeeeee" width="95%">
<thead>
<tr bgcolor="#cecece">
<th width="20%">MBean Requirement</th>
<th>callback</th>
<th>use case example</th>
</tr>
</thead>
<tbody>
<tr>
<td bgcolor="#dedede">get a reference to the MBeanServer</td>
<td><code>preRegister</code></td>
<td bgcolor="#fafafa">The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> needs a reference
to the MBeanServer in order to create and
register other MBeans, such as the
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManagerMXBean.html"
title="The ResultLogManagerMXBean is in charge of managing result logs"
>ResultLogManagerMXBean</a>, and the
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
title="A DirectoryScannerMXBean looks for file matching a given set of criteria, starting at a given root."
>DirectoryScannerMXBeans</a>.
</td>
</tr>
<tr>
<td bgcolor="#dedede">reject registration if conditions are
not met.
</td>
<td><code>preRegister</code></td>
<td bgcolor="#fafafa">The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> will throw
an IllegalArgumentException in <code>preRegister</code>
if the ObjectName it is being passed is
illegal. Throwing an exception in
<code>preRegister</code> makes the registration fail.
</td>
</tr>
<tr>
<td bgcolor="#dedede">get my client-assigned MBean name</td>
<td><code>preRegister</code></td>
<td bgcolor="#fafafa">The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a> propagates the
value of the <code>name=</code> property of
the ObjectName it is given into its
ScanManagerConfig bean.
</td>
</tr>
<tr>
<td bgcolor="#dedede">provide my own default ObjectName if none
was given to the MBeanServer
</td>
<td><code>preRegister</code></td>
<td bgcolor="#fafafa">The name that is returned by <code>preRegister</code>
is the ObjectName with which the MBean will be
eventually registered.
The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a> is able to suggest
a value for its own ObjectName if none was
provided. Similarly, the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a>
always returns its singleton ObjectName
defined by <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html#SCAN_MANAGER_NAME"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean.SCAN_MANAGER_NAME</a>.
</td>
</tr>
<tr>
<td bgcolor="#dedede">perform initialization steps</td>
<td><code>preRegister</code></td>
<td bgcolor="#fafafa">The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a> uses <code>preRegister</code>
to initialize its internal ScanManagerConfig bean.
</td>
</tr>
<tr>
<td bgcolor="#dedede">perform initialization steps, once it is
known that the registration was successful.
</td>
<td><code>postRegister</code></td>
<td bgcolor="#fafafa">The <code>postRegister</code> method
can be used to implement
initialization steps that need to be done once it
is known that the registration was successful, or to
undo any action performed by <code>preRegister</code> once it
is known that registration was not successful.
The <code>postRegister</code> method has a Boolean parameter
which tells the MBean whether it was or wasn't
successfully registered in the MBeanServer.
The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> uses <code>postRegister</code> to create
and register other MBeans, such as the
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManagerMXBean.html"
title="The ResultLogManagerMXBean is in charge of managing result logs"
>ResultLogManagerMXBean</a> and the default
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a>.
Note that <code>postRegister</code> is not expected to throw any
exception. If an exception needs to be thrown, it should
be thrown in <code>preRegister</code>.
</td>
</tr>
<tr>
<td bgcolor="#dedede">check whether the MBean can be deregistered</td>
<td><code>preDeregister</code></td>
<td bgcolor="#fafafa">The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> uses this method to verify
that its state allows it to be deregistered.
In particular, it will refuse to be deregistered
if it is in the RUNNING or SCHEDULED state.
If <code>preDeregister</code> throws an exception, the unregisterMBean
call will fail and the MBean will remain registered in
the MBeanServer.
Take particular care when implementing business logic
in this method: if the logic you implement has an
unfortunate bug which makes it always throw an
exception, you will never be able to unregister
that MBean.
</td>
</tr>
<tr>
<td bgcolor="#dedede">clean up resources, refusing to be deregistered if
it fails
</td>
<td><code>preDeregister</code></td>
<td bgcolor="#fafafa">The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> uses this method to unregister
all the other MBeans it has created and registered in the
MBeanServer. This includes the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManagerMXBean.html"
title="The ResultLogManagerMXBean is in charge of managing result logs"
>ResultLogManagerMXBean</a>, the
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBeans</a> it has created, and the
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
title="A DirectoryScannerMXBean looks for file matching a given set of criteria, starting at a given root."
>DirectoryScannerMXBeans</a> it has created when
applying its configuration.
</td>
</tr>
<tr>
<td bgcolor="#dedede">clean up resources which need to be released in
a best-effort way, when it is known that the MBean is no
longer registered.
</td>
<td><code>postDeregister</code></td>
<td bgcolor="#fafafa"><code>postDeregister</code> is only called if the MBean was succesfully
unregistered.
The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> uses this method to cancel
its internal java.util.Timer.
</td>
</tr>
</tbody>
</table>
</center><br></p>
</ul>
<h3>The Singleton MBean Pattern</h3>
<ul>
<p>
A singleton MBean is an MBean which can only have one
instance registered in a given MBeanServer. <br>
A singleton MBean usually has a well-known name,
which can be defined as a constant. In that case,
clients no longer need to call <code>new ObjectName(...)</code>
and catch the declared <code>MalformedObjectNameException</code>.
</p>
<p>There are already quite a few examples of singleton
MBeans in the java.lang.management API. The
ThreadingMXBean, ClassLoadingMXBean, RuntimeMXBean, etc.
are all singleton MBeans.
</p>
<p>In this example, we have two singleton MBeans:
The <code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a></code> and the
<code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManagerMXBean.html"
title="The ResultLogManagerMXBean is in charge of managing result logs"
>ResultLogManagerMXBean</a></code>. But in fact,
the only real singleton MBean is the
<code>ScanManagerMXBean</code>. The
<code>ResultLogManagerMXBean</code> just happens to
be a singleton MBean because it has a 1-1 relationship
with the <code>ScanManagerMXBean</code>.
</p>
<p>The <code>ScanManagerMXBean</code> implements the
singleton MBean pattern in this way:
</p>
<ul>
<li>The <code>ScanManagerMXBean</code> name has a single
key property: <code>type=ScanManagerMXBean</code>.</li>
<li>Its name is defined by an ObjectName constant called
<code>SCAN_MANAGER_NAME</code> in the <code>ScanManager</code> class</li>
<li>The <code>ScanManagerMXBean</code> enforces its status of
singleton MBean. It will refuse to be registered
with a name other than
the <code>SCAN_MANAGER_NAME</code>. You can therefore depend on
the fact that the <code>ScanManagerMXBean</code> will always
be registered with its singleton <code>SCAN_MANAGER_NAME</code>
(see <code>preRegister</code>)
</li>
<li>You are not obliged to provide a name when you
register the <code>ScanManagerMXBean</code>: if you pass null,
then the <code>ScanManager</code> will be registered with
its singleton <code>SCAN_MANAGER_NAME</code>
(see <code>preRegister</code>).
</li>
<li>The <code>ScanManager</code> class has a no-arg static
<code>register</code> method that will register
the singleton instance in the Platform MBeanServer.
This static <code>register</code> method returns
a proxy to the registered singleton.
</li>
<li>The <code>ScanManager</code> class has also a static
<code>register</code> method that will create
a singleton instance in a (possibly remote)
MBeanServerConnection - using
<code>createMBean</code>.
This static <code>register</code> method
also returns a proxy to the registered singleton.
</li>
<li>Only the MBeanServer has a reference to the
singleton instance. The singleton instance is
not returned to the caller, and not kept
in any other static data structure.
</li>
</ul>
<p>
On the other hand, the <code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManagerMXBean.html"
title="The ResultLogManagerMXBean is in charge of managing result logs"
>ResultLogManagerMXBean</a></code>
has a much more relaxed implementation of the pattern:
<br>It simply provides its own singleton name if it is
registered with a null ObjectName, but will not enforce
the use of that name.
</p>
<p>Note that all singleton MBean names in this example
are created using the <code>ScanManager.makeSingletonName</code>
method, which implements the pattern for ObjectNames suggested
in the JMX Best Practices.
</p>
</ul>
<h3>Managing the Life Cycle of dependent MBeans</h3>
<ul>
<p>A common task that many JMX applications have
is to manage the life cycle of MBeans registered
in the MBeanServer.</p>
<p>In this example, we have decided to follow a simple
pattern:</p>
<ul>
<li>The application is initialized simply
by registering the singleton
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> in
the MBeanServer.
</li>
<li>The <code>ScanManagerMXBean</code> will then
in turn register any other MBean that the
application might need:
<ul>
<li>It creates and registers the singleton
<code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManagerMXBean.html"
title="The ResultLogManagerMXBean is in charge of managing result logs"
>ResultLogManagerMXBean</a></code>
</li>
<li>It creates and registers the default
<code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a></code>
which loads the initial configuration
</li>
<li>It creates as many
<code><a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
title="A DirectoryScannerMXBean looks for file matching a given set of criteria, starting at a given root."
>DirectoryScannerMXBeans</a></code> as
needed when the configuration is applied
</li>
<li>It lets you create alternate
<code>ScanDirConfigMXBean</code>, to
which you can later switch in order
to apply a new alternate configuration.
</li>
</ul>
</li>
<li>When a new configuration is applied (or if the
current configuration is reapplied), the
<code>ScanManagerMXBean</code> will unregister
any <code>DirectoryScannerMXBeans</code> it has
previously registered, and will re-create
brand new <code>DirectoryScannerMXBeans</code>
from the applied configuration.
</li>
<li>When you unregister the <code>ScanManagerMXBean</code>,
it does all the cleanup for you, by unregistering
all the MBeans that it has created during the
course of the application.
</li>
</ul>
<p>The <code>ScanManagerMXBean</code> makes use of its
<code>MBeanRegistration</code> interface in order
to register the other MBeans it needs (see the
<code>ScanManager.postRegister</code> method) and to unregister
every MBean it has created (see the <code>ScanManager.preDeregister</code>
method).
</p>
<p>You will note that the <code>ScanManagerMXBean</code>
will only allow itself to be deregistered if it can be
closed - that is if there's no other action in
progress.
This is to make sure that the deregistration of
dependent MBeans will work smoothly.
<br>
The deregistration of related MBeans will happen
in the <code>ScanManager.preDeregister</code>
method.
<br>
If one of these MBeans could not be deregistered,
then the <code>ScanManagerMXBean</code> will throw
an exception, refusing to be deregistered.
<br>This leaves you a chance to try to deregister it
again later. Since the <code>ScanManagerMXBean</code>
has switched its state to CLOSED before starting
to unregister its dependent MBeans, it will refuse
any further actions, ensuring that e.g. nobody
can try to start it or schedule it while it
is in that partially-deregistered state.
</p>
<p>Handling the LifeCycle of all the application's
MBeans in a single MBean is usually a good design
pattern, especially if the application is a
module which is intended to share a JVM - or
an MBeanServer - with other modules.
</p>
<p>This is specially useful if the application needs to
be loaded and unloaded on demand: in that
case, simply registering or unregistering the top level
MBean (in our example the <code>ScanManagerMXBean</code>) does
the trick.
</p>
</ul>
<h3>Emitting Notifications</h3>
<ul>
<p>In order to emit notifications, an MBean must be
an instance of <code>NotificationEmitter</code>.
The <code>NotificationEmitter</code> interface defines methods
that the MBeanServer will call on the MBean in order
to register <code>NotificationListeners</code> with the MBean.
</p>
<p>It is worth noting that the MBean may not be
invoked each time a JMX client wants to register
a listener. For instance, the RMIConnectorServer
registers <i>only once</i> a single listener with each MBean
which is a <code>NotificationEmitter</code>.
In that specific case, the listener may even be registered
with the MBean before any client has actually subscribed
for notifications from that particular MBean.
</p>
<p>An MBean can therefore make no assumption about
which client or how many clients have registered for
notifications.
</p>
<p>It is also worth noting that the logic of the
methods defined in <code>NotificationEmitter</code> would not
be trivial to implement from scratch. Fortunately
the JMX API defines a helper class, called
<code>NotificationBroadcasterSupport</code>, which
provides an implementation for these methods.
</p>
<p>There are actually three ways for an MBean to
implement <code>NotificationEmitter</code>, of which only two
are recommended.
</p>
</ul>
<h4>Extending NotificationBroadcasterSupport</h4>
<ul>
<p>This is the simplest way of coding an MBean which
is a <code>NotificationEmitter</code>:
</p>
<p>Simply extend <code>NotificationBroadcasterSupport</code>,
then override its <code>getNotificationInfo</code> method
which returns the <code>MBeanNotificationInfo[]</code> array
that should be included in your MBean's <code>MBeanInfo</code>
and that's it.
<br>You just need to call the <code>sendNotification</code> method
inherited from <code>NotificationBroadcasterSupport</code> whenever
your MBean needs to send a notification.
</p>
<p>In our example, both the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a> and <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManagerMXBean.html"
title="The ResultLogManagerMXBean is in charge of managing result logs"
>ResultLogManagerMXBean</a> extend
<code>NotificationBroadcasterSupport</code> in order
to send notifications.
</p>
</ul>
<h4>The Delegation Pattern: delegating to a
NotificationBroadcasterSupport delegate</h4>
<ul>
<p>There may be cases however where delegating to a
wrapped <code>NotificationBroadcasterSupport</code>
object may be preferred to extending
<code>NotificationBroadcasterSupport</code>.
</p>
<p>For instance, if your MBeans already derive from
some base class, extending <code>NotificationBroadcasterSupport</code>
might not be an option.
</p>
<p>Similarly, if you do not want to have the inherited
<code>public void sendNotification(Notification notification)</code>
method appear in the Javadoc of the concrete class of your
MBean, you may want to consider using the delegation
pattern instead of extending
<code>NotificationBroadcasterSupport</code>
</p>
<p>In our example both the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> and the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
title="A DirectoryScannerMXBean looks for file matching a given set of criteria, starting at a given root."
>DirectoryScannerMXBean</a> use the delegation
pattern rather than extending
<code>NotificationBroadcasterSupport</code>.
In the end, choosing between one or the other method
is more a question of taste, although the delegation
pattern could be considered more flexible since it
doesn't require extending any given superclass.
</p>
<p>It may be also worth noting that some tools like
the JMX Module of <a
href="http://www.netbeans.org"
>NetBeans IDE</a>, will be able to
generate for you all the code that delegates to a
wrapped <code>NotificationBroadcasterSupport</code>.
</p>
</ul>
<h4>Implementing NotificationEmitter from scratch</h4>
<ul>
<p>This is the last possibility for an MBean that
needs to send notifications: simply implement
<code>NotificationEmitter</code> from scratch. This is highly
discouraged since that logic is not trivial, and
already provided by
<code>NotificationBroadcasterSupport</code> anyway.
</p>
</ul>
<h4>Beware of Synchronization Locks</h4>
<ul>
<p>One thing you must keep in mind when sending
notifications is not to send them from within
a synchronized block, or while holding a lock on
some resource.</p>
<p>Indeed, what happens when you send a notification
may vary greatly depending on whether the client
which has registered for notifications has done
so through a <code>JMXConnector</code> (like the
<code>JMXRMIConnector</code>)
or through a direct reference to the MBeanServer
(by calling
<code>MBeanServer.addNotificationListener</code>).
</p>
<p>In this latter case, the listener will be invoked
synchronously in the same thread that your MBean is
using to send its notification. If by misfortune, the
code of that listener now re-enters your MBean through a
call that flows through a JMXConnector, a deadlock
could occur. It is therefore very important to release
any lock you may have before calling
<code>sendNotification</code>.</p>
<p>An easy way to do that is demonstrated in the
<code>ScanManager</code> class. The ScanManager
has an internal private queue of pending notifications.
When a notification needs to be sent (e.g. because the
ScanManager state is being switched), the notification
is simply prepared and put into the pending notification
queue.
The notification queue is then processed later on,
at the end of the method, when the processing is finally
completed and all the locks have been released.
<br>At this point the notification queue might already
have been emptied by another thread - in which case
the pending notifications will have already been
removed from the queue. Which thread actually gets
to send the notifications is of no importance. The
important point is that all the locks detained by
your MBean code in that thread were released before
the notification was sent.
</p>
<p>In our example the <code>ScanManager</code> class
ensures this by:
<ul>
<li>Only calling <code>sendNotification</code>
in its private <code>sendQueuedNotifications</code>
method.
</li>
<li>Only calling <code>sendQueuedNotifications</code>
when all locks have been released.
</li>
<li>Never calling a method that calls
<code>sendQueuedNotifications</code> from within
a synchronized block.</li>
</ul>
</p>
</ul>
<h4>Don't subclass Notification</h4>
<ul>
<p>Another common best practice when you want
to improve interoperability is to use directly
the Notification base classes provided in the
JMX<sup>TM</sup> API. Do not create your own
subclasses of these standard classes.
</p>
<p>Indeed, if you code your own subclass, a generic
client, like jconsole, will not be able to receive
that notification unless it has that custom
subclass in its classpath.
</p>
<p>
If you want your application to be interoperable, it is
therefore preferable not to subclass any of the standard
Notification classes. You can define your own
Notification type string, and if you need to send
additional data, you can put a CompositeData, or a
HashMap of serializable standard types in the
Notification's user data fields.
</p>
<p>In this example, we are using directly the
standard notification classes:
<ul>
<li>The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> and the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
title="A DirectoryScannerMXBean looks for file matching a given set of criteria, starting at a given root."
>DirectoryScannerMXBean</a> both use directly
<code>AttributeChangeNotification</code> to notify
changes in their <code>State</code> attribute.
</li>
<li>The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
title="A DirectoryScannerMXBean looks for file matching a given set of criteria, starting at a given root."
>DirectoryScannerMXBean</a>
also uses the base <code>Notification</code>
class directly in order to notify whenever
it finds a matching file.
<br>In that case, we simply use the base
<code>Notification</code>
class with a new
<b><code>com.sun.jmx.examples.scandir.filematch</code></b>
type.
</li>
<li>The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a> and <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManagerMXBean.html"
title="The ResultLogManagerMXBean is in charge of managing result logs"
>ResultLogManagerMXBean</a> also both use the base
<code>Notification</code> class.
</li>
</ul>
<p>Careful readers will have noted that the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> and the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
title="A DirectoryScannerMXBean looks for file matching a given set of criteria, starting at a given root."
>DirectoryScannerMXBean</a> both use the
<code>AttributeChangeNotification</code> class
to notify about their state change, whereas the
<a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a> uses the base
<code>Notification</code> class.
</p>
<p>In fact, this is because the semantics of these
notifications is not exactly the same - although
both denote a state change:
<ul>
<p>In the case of <code>ScanManagerMXBean</code>
and <code>DirectoryScannerMXBean</code>, the
notification which is emitted is more about a
state transition, from one state to another.
For instance, going from <code>RUNNING</code>
to <code>STOPPED</code>, or from
<code>SCHEDULED</code> to <code>STOPPED</code>.
<br>In that case, the
<code>AttributeChangeNotification</code> was
more appropriate because it made it possible
to send the previous and the new value of the
state attribute, thus reflecting the whole
state transition.
</p>
<p>In the case of the <code>ScanDirConfigMXBean</code>
however, what is of interest is the state in
which the MBean has arrived. Using the base
<code>Notification</code> class with three different
notification type strings -
<b><code>com.sun.jmx.examples.scandir.config.loaded</code></b>,
<b><code>com.sun.jmx.examples.scandir.config.modified</code></b>,
and
<b><code>com.sun.jmx.examples.scandir.config.saved</code></b> -
was therefore closer to what we wanted to model.
</p>
</ul>
</p>
</ul>
<h3>Configuration MBeans</h3>
<ul>
<p>A common practice when designing a management application is
to have an MBean, or a set of MBeans, dedicated to configuration.
Separating configuration from control and monitoring allows
more appropriate logic, and often simplifies the design and
implementation of the management interface.
</p>
<p>
In our example, the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a> is dedicated to the application configuration.
</p>
<p>The <code>ScanDirConfigMXBean</code> will let you interactively
modify, save, or load the application configuration. The modifications
will not be taken into account until it is applied, by invoking
<code>applyConfiguration</code> on the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a>.
It is also possible to create many configurations, by creating as
many <code>ScanDirConfigMXBean</code>s, and then to choose and apply
one of these configurations by calling
<code>ScanManagerMXBean.setConfigurationMBean</code> and then
<code>ScanManagerMXBean.applyConfiguration</code>.
</p>
<p>In this way, all configurations aspects are gathered and concentrated
inside the <code>ScanDirConfigMXBean</code> instead of being scattered
throughout all the MBeans that compose the application.
</p>
<p>In order to save and store the application configuration data, the
<code>ScanDirConfigMXBean</code> uses a set of XML serializable Java beans
defined in the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/config/package-summary.html"
title="The com.sun.jmx.examples.scandir.config package defines XML serializable beans"
>com.sun.jmx.examples.scandir.config</a> package. These beans are very
simple Java beans which have been lightly annotated for XML binding.
</p>
<p>It is worth noting that these same beans can also be handled by the
MXBean framework (our beans don't contain recursive data structures) and can
therefore be used directly as attributes and parameters of MXBeans, without
needing to be Java-serializable (the MXBean framework transform them in
CompositeData objects - which <b>are</b> serializable).
</p>
<p>The same <a
href="dist/javadoc/com/sun/jmx/examples/scandir/config/ScanManagerConfig.html"
title="The com.sun.jmx.examples.scandir.config package defines XML serializable beans"
>ScanManagerConfig</a> bean that we use to read from and write to the
XML configuration file is thus also used as attribute of the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
title="The ScanDirConfigMXBean is in charge of the configuration"
>ScanDirConfigMXBean</a>. It is transformed into a <code>CompositeData</code>
by the MXBean framework, and can be easily introspected with
<a href="#JConsole">jconsole</a>.
</p>
</ul>
<h3>MBeans Must Be Thread-Safe</h3>
<ul>
<p>A question often asked by newcomers to JMX technology
is whether the MBeanServer is thread-safe. Well, the MBeanServer <b>is</b>
thread safe, but it doesn't put any locks on the MBeans it contains. The
MBeans can be concurrently accessed by multiple threads, and must therefore
take care of their own thread safety.
</p>
<p>In this example, we have been using two methods to ensure thread
safety for our MBeans: synchronized blocks, and semaphores.
</p>
<p>Using synchronized blocks is probably the most common and easiest way
to implement thread safety in Java. When dealing with MBeans though, here
are a couple of rules to keep in mind:
<ul>
<li>Don't send notifications from within a synchronized block: there's
no way to tell whether the listener's code will be executed in the
same thread or a different thread, and holding a lock in these
conditions is therefore dangerous, as it could lead to deadlocks.</li>
<li>Also avoid invoking another MBean from a synchronized block
unless you are completely in control of both MBeans, and you can
ascertain that it won't lead to any deadlock. Indeed, if you invoke an
MBean exposed by another application, it can be sometime hard to
know with certainty whether holding a lock while invoking that
MBean will have any side effect. Maybe that MBean will make
further calls to other MBeans which will in turn try to call
your MBean, or maybe it will emit a
notification, and we'll be back to the considerations just
above.</li>
</ul>
</p>
<p>Another means of implementing thread-safe code is to use semaphores.
The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> uses a semaphore called
<code>sequencer</code> to ensure
that critical code sections are not executed concurrently. In this
MBean, we use <code>Semaphore.tryAcquire</code> to lock the sequencer
semaphore before entering the critical section. If the
<code>Semaphore.tryAcquire</code> returns true then we enter the critical
section. If it returns false, we throw an IllegalStateException, stating
that we couldn't acquire the lock. The code looks like this:
<pre>
if (!sequencer.tryAcquire())
throw new IllegalStateException("resource locked");
try {
// critical code here ...
} finally {
// Always use try/finally to ensure that the semaphore
// will be released, even if exceptions or errors are raised!
sequencer.release();
}
</pre>
</p>
<p>Using <code>Semaphore.tryAcquire</code> and throwing an exception if
the semaphore is already locked makes it safer to call other MBeans
from within the critical section: in potential deadlock situations
the calling code will get the <code>IllegalStateException</code>
instead of being blocked on the deadlocked lock.
</p>
<p>It is worth noting that each of these techniques has its own
advantages and disadvantages - which can make one of them more or less
appropriate depending on the inner logic of the MBean you're implementing.
</p>
<p>Careful readers will also have noted that we used
<code>IllegalStateException</code> directly, instead of defining
our own subclass of RuntimeException, which could have had a more
precise semantics. If you define a new exception for your JMX application,
you must keep in mind that your client will need to have the class
of your exception in its classpath to get that exception.
Otherwise your client will get a completely different exception, indicating a
deserialization issue.
</p>
</ul>
<h3>Waiting for Notifications</h3>
<ul>
<p>Implementing code that needs to wait for notifications is sometimes
difficult. Because notifications are asynchronous, doing something
like:
<pre>
// register a notification listener
...
// start a management action
...
// wait for a notification
...
// do something based on whether the expected notification
// is received
...
</pre>
is not always trivial. However, there's a very easy way to do that: use
a blocking queue of notifications.
<pre>
final BlockingQueue&lt;Notification&gt; notifQueue =
new LinkedBlockingQueue&lt;Notification&gt;();
final NotificationListener listener = new NotificationListener() {
public void handleNotification(Notification notification,
Object handback) {
try {
// Just put the received notification in the queue.
// It will be consumed later on.
//
notifQueue.put(notification);
} catch (InterruptedException ex) {
// OK
}
}
};
// register the listener - possibly also as a JMXConnectionNotification
// listener to get Notification Lost notification
...
// start management action
...
// wait for notification
while (expected notif not received and delay not expired) {
Notification n = notifQueue.poll(3,TimeUnit.SECONDS);
// if expected notif, do something
...
}
// if expected notification not received do something else.
....
</pre>
</p>
<p>You will note that this is a technique we've been using in the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirAgent.html"
title="The ScanDirAgent class defines a main method for the scandir application"
>ScanDirAgent</a> class and in the example unit tests.
</p>
</ul>
<h3>Holding hard references to other MBeans: proxy or direct reference?</h3>
<ul>
<p>We have seen that MXBeans will let you return proxy references to other
MXBeans. But should that MXBean hold a direct reference to the MXBeans it
relates to, or would it be better for it to hold only a proxy?
</p>
<p>
As a general rule it is better when an MBean reference is
only held by the MBeanServer. It is a better design
to hold a reference to a proxy, rather than to hold
a hard reference to an MBean. However there are two cases
when holding a hard reference might be preferred:
<ol>
<li>When MBean A needs to call a method of method B which
is not part of its MBean interface</li>
<li>When the overhead of going through the MBeanServer
plus the MXBean framework is too great (frequently-called
method, with creation of OpenType)</li>
</ol>
However - holding a hard reference is only advisable
when both MBeans are created by the same piece of code,
and the application can ensure that the life cycle
of each MBean is consistent with regard to the other.
</p>
<p>In our example, the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> holds only proxy references to the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirConfigMXBean.html"
>ScanDirConfigMXBean</a> and the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/DirectoryScannerMXBean.html"
>DirectoryScannerMXBeans</a>. <br>
However it holds a direct reference to the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManager.html"
>ResultLogManager</a>. This makes it possible to pass a direct
reference to the <code>DirectoryScannerMXBeans</code>,
which can then log their results
more efficiently, and would also make it possible to remove
the <code>log</code> method from the <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ResultLogManagerMXBean.html"
>ResultLogManagerMXBean</a> interface - leaving it in the
<code>ResultLogManager</code> class (possibly as a package method)
should we wish to do so.
</p>
</ul>
<h3>Agent Class</h3>
<ul>
<p>The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirAgent.html"
title="The ScanDirAgent class defines a main method for the scandir application"
>ScanDirAgent</a> is the Agent class for the <i>scandir</i> application.
This class contains the <code>main</code> method to start a standalone
<i>scandir</i> application.
</p>
<p>The <code>main</code> method simply registers a <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a> in the platform MBeanServer, and then waits
for someone to call <code>ScanManagerMXBean.close</code>.
</p>
<p>
When the <code>ScanManagerMXBean</code> state is switched to
<code>ScanManagerMXBean.ScanState.CLOSED</code>, the
<code>ScanManagerMXBean</code> is unregistered, and the application
terminates (i.e. the main thread completes).
</p>
<p>Standalone JMX applications usually have an Agent class that contain
their <code>main</code> method, which performs all the MBean
registration steps.
However, it is usually not a bad idea if that class can
be easily turned into an MBean. Indeed, this will make your
application easier to integrate in an environment where it would
no longer be standalone and would no longer control the implementation
of <code>main</code>. In our example the Agent
class could be easily turned into an MBean, exposing its three
<code>init</code>, <code>waitForClose</code> and <code>cleanup</code>
method. However we didn't go as far as turning it into an MBean since
the application can be already easily started by registering an instance
of <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanManagerMXBean.html"
title="The ScanManagerMXBean is the main MBean of the scandir application"
>ScanManagerMXBean</a>.
</p>
</ul>
<h3>Secure Client Class</h3>
<ul>
<p>The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirClient.html"
title="The ScanDirClient class is a very short example of secure programmatic client"
>ScanDirClient</a> is an example class that shows how a
programmatic client can connect to a secured <i>scandir</i> application.
This class contains a <code>main</code> method which creates and
configures a <code>JMXConnector</code> client to connect with
a secured <i>scandir</i> daemon. This class will not work with
the default unsecured agent since it requires mutual authentication.
</p>
<p>How to secure a JMX <i>scandir</i> application and run
the secure <code>ScanDirClient</code> is discussed <a href="#secure"
>later</a> in this document.
</p>
<p>The <code>ScanDirClient</code> is not really part of the
application - and is given here only for the sake of
the example.
</p>
</ul>
<h2><a name="h2-Testing">Testing the <i>scandir</i> Example</a></h2>
<ul>
<p>Make sure that you have access to junit.jar (either 3.8.1 or 3.8.2).
Make sure also that you have junit.jar in your
<code>CLASSPATH</code>.<br>
Then in the example root directory (where the <code>build.xml</code>
file is located) run the following command:
<pre>ant test -Dlibs.junit.classpath=<i><u>path to junit jar (either 3.8.1 or 3.8.2)</u></i></pre>
</p>
<p>Alternatively you can open the jmx-scandir project with the
NetBeans IDE and test the jmx-scandir project from the
<code>Run</code> menu.
</p>
</ul>
<h2><a name="h2-Running">Running the <i>scandir</i> Example</a></h2>
<ul>
<p>In the example root directory (where the <code>build.xml</code>
file is located) run the following commands:
<pre>ant jar
ant run-single -Drun.class=com.sun.jmx.examples.scandir.ScanDirAgent -Djavac.includes=src</pre>
or simply <pre>ant run</pre>
</p>
<p>This will run the example using the configuration
file provided in the src/etc directory.
</p>
<p>Alternatively you can open the jmx-scandir project with the
NetBeans IDE. You can run the example by
selecting the <code>ScanDirAgent</code> file
and run it with <code>Run File</code> in the
<code>Run</code> menu or simply
set the <i>jmx-scandir</i> project as main project and
select <code>Run Main Project</code> from the
main menu. Both targets will use the configuration
file provided in the src/etc directory.
</p>
<p>When the application is started, you can connect to
it with <a href="#JConsole">jconsole</a>.
</p>
<blockquote>
<u>Note:</u> You can also run the <i>scandir</i>
application directly from the <code>java</code>
command line. Make sure to build the project jar
first.
<br>On Unix systems:
<pre>ant jar
java -Djava.util.logging.config.file=logging.properties \
-Dscandir.config.file=src/etc/testconfig.xml \
-jar dist/jmx-scandir.jar</pre>
<br>On Windows systems:
<p><code>ant jar<br>
java &nbsp;-Djava.util.logging.config.file=logging.properties
&nbsp;-Dscandir.config.file=src\etc\testconfig.xml
&nbsp;-jar&nbsp;dist\jmx-scandir.jar</code></p>
</blockquote>
</ul>
<h2><a name="h2-Playing">Playing with JConsole</a></h2>
<ul>
<p>Run the example as explained in the previous section, so
that it uses the provided <code>src/etc/testconfig.xml</code>
configuration file. Then start
jconsole. In the connection window choose the process that runs
<code>com.sun.jmx.examples.scandir.ScanDirAgent</code> or
<code>jmx-scandir.jar</code>.
</p>
<p><center>
<table border="0" cellpadding="2" cellspacing="2">
<tr><td>
<a href="docfiles/connect-local-ant-run.jpg"
title="jconsole connection window - connect to local process"
><img height="440"
src="docfiles/connect-local-ant-run.jpg"
alt="jconsole connection window - connect to local process"
/></a>
</td>
<td>
<a href="docfiles/connect-local-java-jar.jpg"
title="jconsole connection window - connect to local process"
><img height="440"
src="docfiles/connect-local-java-jar.jpg"
alt="jconsole connection window - connect to local process"
/></a>
</td></tr></table>
</center>
</p>
<p>Open the MBeans tab, and look for the
<code>ScanDirConfigMXBean</code>.
Click on its <code>Attributes</code> node and double click on its
<code>Configuration</code> attribute, to look at
the loaded configuration - values in bold can
be expanded by a double-click.
</p>
<p><center><a href="docfiles/scandir-config.jpg"
title="jconsole MBean tab: ScanDirConfigMXBean"
><img
src="docfiles/scandir-config.jpg"
alt="jconsole MBean tab: ScanDirConfigMXBean"
/></a></center>
</p>
<p>Now go to the <code>ScanManagerMXBean</code>, click on
its <code>Notifications</code> node, and subscribe
for notifications. Then click on the
<code>Operations</code> node and invoke the
<code>start()</code> operation:
</p>
<p><center><a href="docfiles/scandir-start.jpg"
title="jconsole MBean tab: ScanDirConfigMXBean"
><img
src="docfiles/scandir-start.jpg"
alt="jconsole MBean tab: ScanDirConfigMXBean"
/></a></center>
</p>
<p>You can see that the notifications counter was
incremented by three: you have just scheduled,
run, and completed a batch of directory scans.
</p>
<p>Now go to the <code>ResultLogManagerMXBean</code>,
click on its <code>Attributes</code> node, and
expand its <code>MemoryLog</code> attribute:
</p>
<p><center><a href="docfiles/scandir-result.jpg"
title="jconsole MBean tab: ScanDirConfigMXBean"
><img
src="docfiles/scandir-result.jpg"
alt="jconsole MBean tab: ScanDirConfigMXBean"
/></a></center>
</p>
<p>You can see that the directory scan results have
been logged.</p>
<p>To make the application terminate go back to the
<code>ScanManagerMXBean</code> and invoke
<code>close()</code>. The <code>ScanDirAgent</code>
will receive the notification, step out of
the application main thread, and the application
will terminate.
</p>
<p>This is of course a very limited scenario. Feel free
to improvise with all the features of the example, creating
a new configuration -
<code>ScanManagerMXBean.createOtherConfigurationMBean</code> -
adding multiple directory scanners to that configuration -
<code>ScanDirConfigMXBean.addDirectoryScanner</code> -
then switching the <code>ScanManagerMXBean</code> current
configuration by changing the value of the <i>ConfigurationMBean</i>
attribute - <code>ScanManagerMXBean.setConfigurationMBean</code>
- then applying the new configuration -
<code>ScanManagerMXBean.applyConfiguration(true)</code> -
then scheduling repeated directory scans every 10 seconds -
<code>ScanManagerMXBean.schedule(0,10000)</code> -
subscribing for notifications, etc...
</p>
</ul>
<a name="secure"></a>
<h2><a name="h2-Turning">Turning the example into a Secure JMX Application</a></h2>
<ul>
<p>In this section, we will see how to configure and
start the <i>scandir</i> example so that the JVM agent
is bootstrapped with a secure JMXConnectorServer. Indeed, until
now we have only used the insecure local connection,
which can only be used as long as both the client and
the server run on the same machine. This section will
explain how to start the <code>ScanDirAgent</code> so
that a real secure RMIConnectorServer is started at bootstrap.
</p>
<p>To achieve this we will: <a href="#management.properties"
>provide our own management.properties</a>, <a
href="#password-access">create our own password and access files</a>,
<a href="#keystore-truststore">provide a keystore and truststore</a>,
<a href="#start-secure-agent">start the ScanDirAgent with the
appropriate system properties</a>.
</ul>
<h3>Configuring the JVM Agent for Secure Remote Connection</h3>
<ul>
<p>The easiest way to <a name="management.properties">configure the
JVM Agent</a> for Secure Remote
Connection is to use your own <a
href="http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html#properties"
title="This page describes the properties you can put in your management.properties file"
>management.properties</a> file.
In this example, we have copied the default
<code>$JRE/lib/management/management.properties</code>
file to the example's <code>src/etc</code> directory and
modified it in <a href="src/etc/management.properties"
title="our modified management.properties"
>this way</a>:
<ul>
<li>We have set the RMI port to <u>4545</u> - this is just a
random port number we have picked up. Feel free to use your
own value suited to your environment.
<pre># For setting the JMX RMI agent port use the following line
com.sun.management.jmxremote.port=<b>4545</b></pre>
</li>
<li>We have <u>switched on</u> SSL <u>mutual authentication</u>
<pre># For RMI monitoring with SSL client authentication use the following line
com.sun.management.jmxremote.ssl.<b>need.client.auth</b>=<b>true</b></pre>
</li>
<li>We have also <u>secured the RMI Registry</u> with SSL
<pre># For using an SSL/TLS <b>protected</b> RMI Registry use the following line
com.sun.management.jmxremote.<b>registry.ssl</b>=<b>true</b></pre>
</li>
<li>We have provided <a
href="src/etc/password.properties">our own password file</a>
<pre># For a non-default password file location use the following line
com.sun.management.jmxremote.password.file=<i>src/etc/password.properties</i></pre>
</li>
<li>We have provided <a
href="src/etc/access.properties">our own access file</a>
<pre># For a non-default password file location use the following line
com.sun.management.jmxremote.access.file=<i>src/etc/access.properties</i></pre>
</li>
</ul>
<p>You will note that we haven't provided any value
for the other security properties, like
<code>com.sun.management.jmxremote.authenticate=true</code>,
because these properties already default to a value
which enables security by default.
Note however that protecting the RMI Registry with SSL
improves the application security, but only as long as
mutual authentication is also switched on. Otherwise, just
anybody would be able to connect to the registry and
get the RMIServer stub.
</p>
<p>We do recommend that you <u>use the most secure configuration
when you deploy a JMX agent</u> - which means <u>switching on
SSL protection for the RMI registry</u> <b>and</b> <u>requiring
mutual authentication</u>, as we show in this example.
</p>
<p>We will use the <code>com.sun.management.config.file</code>
system property to pass our <a
href="src/etc/management.properties">management.properties</a>
file to the <code>ScanDirAgent</code>.
</p>
</ul>
<h3>Creating a password and access file</h3>
<ul>
<p>As explained above, we have created our own
<a href="src/etc/password.properties">password file</a>
and <a href="src/etc/access.properties">access file</a>
for <a name="password-access">access control and authorization</a>.
</p>
<p>In the password file, we have defined two logins:
<i>guest</i> and <i>admin</i>. The password for
<i>guest</i> is <i>guestpasswd</i> and the password
for <i>admin</i> is <i>adminpasswd</i>.
</p>
<p>In the access file, we have mapped these two logins
to access rights: the <i>admin</i> login has <i>read-write</i>
access, while the <i>guest</i> login only has <i>read-only</i>.
</p>
<p>Before starting the <code>ScanDirAgent</code>, you will
need to restrict access permission to the password file,
in such a way that nobody but you can read it. Otherwise, the
JVM Agent will refuse to start the JMXConnectorServer, as it will
fear that security can be compromised if other parties can
have read access to the password file. How to restrict
read access to the password file is explained in detail
<a href="http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html#PasswordAccessFiles"
title="Using Password and Access Files"
>here</a>.
</p>
<p>As we have seen above, the location
of our access and password files is configured in our own <a
href="src/etc/management.properties">management.properties</a>
file.
</p>
</ul>
<h3>Keystore and Truststore</h3>
<ul>
<p>Using SSL with mutual authentication means that both
client and server will need a <a name="keystore-truststore"
>keystore and a truststore</a>
to store their own certificates, and the certificates of
the parties they trust. Usually, client and server will
have their own keystore and truststore.
</p>
<p>For the sake of simplicity - and to get you started
without the tedious necessity of creating your own keystore
and truststore, we are providing a dummy keystore and
truststore, containing a certificate self-signed by duke.
The password for our keystore is <i>password</i>, and the
password for our truststore is <i>trustword</i>.
We suggest that you first get the example running with the
keystore and truststore we are providing before attempting
to use your own keystore and truststore.
</p>
<p>A secure application will obviously need to use its own
keystore and truststore, <b><u>and should not rely on the keystore
and truststore we are providing here!</u></b>
</p>
<p>How to create your own keystore and truststore, is explained
in <a
href="http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html#SSL_enabled"
title="Monitoring and Management Using JMX"
>here</a>.
As shown <a href="#start-secure-agent">later</a>,
we will need to use <a
href="http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html#SSL_enabled"
>system properties</a> to pass our truststore
and keystore to the <code>ScanDirAgent</code>.
</p>
</ul>
<h3>Starting a Secure <i>scandir</i> agent</h3>
<ul>
<p>To start a <a name="start-secure-agent"
>secure <i>scandir</i> agent</a>, go to the
<i>scandir</i> example root directory and type the
following command:</p>
<p>On Unix Systems:
<pre>ant jar
java \
-Djava.util.logging.config.file=logging.properties \
-Djavax.net.ssl.keyStore=keystore \
-Djavax.net.ssl.keyStorePassword=password \
-Djavax.net.ssl.trustStore=truststore \
-Djavax.net.ssl.trustStorePassword=trustword \
-Dcom.sun.management.config.file=src/etc/management.properties \
-Dscandir.config.file=src/etc/testconfig.xml \
-jar dist/jmx-scandir.jar</pre>
</p>
<p>On Windows Systems:
<p><code>ant jar<br>
java
&nbsp;-Djava.util.logging.config.file=logging.properties
&nbsp;-Djavax.net.ssl.keyStore=keystore
&nbsp;-Djavax.net.ssl.keyStorePassword=password
&nbsp;-Djavax.net.ssl.trustStore=truststore
&nbsp;-Djavax.net.ssl.trustStorePassword=trustword
&nbsp;-Dcom.sun.management.config.file=src\etc\management.properties
&nbsp;-Dscandir.config.file=src\etc\testconfig.xml
&nbsp;-jar&nbsp;dist\jmx-scandir.jar</code></p>
</p>
<p>If you start jconsole now, you will see that you
are still able to connect to the agent using the
local connection. However, if you try to connect
through the remote connector, using
<a href="docfiles/remote-connection.jpg">localhost:4545</a>,
the connection will <a href="docfiles/remote-connection-failed.jpg"
>fail</a>, even if you provide a correct login/password
pair. Indeed, since the JMXConnectorServer is now protected with SSL,
jconsole must also be configured with the appropriate SSL parameters
so that it can authenticate the server and get authenticated by the
server too as the SSL configuration of the server requires mutual
authentication.
</p>
<p>The next section will discuss how to connect to the
secure agent.
</p>
</ul>
<h2><a name="h2-Connecting">Connecting to the Secure JMX Application</a></h2>
<ul>
<p>We will now see how to connect to the secure agent,
using jconsole, and using a programmatic client.
</p>
</ul>
<h3>Using jconsole to connect to the secure agent</h3>
<ul>
<p>The only special thing you need to do in order to
be able to connect to your secure agent with
jconsole, is to give it a keystore (containing
its client certificate) and a truststore (containing
the certificates of the servers it can trust).
In our example, we use the same keystore/truststore
pair on the client and server side - but this is
not what a real application would do.
Indeed a real application would have different
certificates for the client and the server, and
thus use different keystores (and probably truststores).
More information on SSL authentication can be obtained from the <a
href="http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#HowSSLWorks"
title="How SSL Works"
>Java<sup>TM</sup> Secure Socket Extension (JSSE) Reference Guide</a>.
</p>
<p>To start jconsole with our provided keystore and
truststore, go to the scandir example root directory and
type in the following command:
<p><code>jconsole
&nbsp;-J-Djava.util.logging.config.file=logging.properties
&nbsp;-J-Djavax.net.ssl.keyStore=keystore
&nbsp;-J-Djavax.net.ssl.keyStorePassword=password
&nbsp;-J-Djavax.net.ssl.trustStore=truststore
&nbsp;-J-Djavax.net.ssl.trustStorePassword=trustword</code></p>
</p>
<p>The <code>-J-Djava.util.logging.config.file=logging.properties</code>
flag is not mandatory, but passing a <code>logging.properties</code>
may help you debug connection problems if anything goes wrong.
</p>
<p>In jconsole connection window, choose to connect to a
remote process, using the address <i>localhost:4545</i>
and the guest login:
</p>
<p><center><a href="docfiles/remote-connection.jpg"
><img src="docfiles/remote-connection.jpg"
alt="jconsole connection window"/></a></center>
</p>
<p>You will see that the agent will let view all the
MBeans and their attributes, but will reject any
attribute modification or remote method invocation.
</p>
<hr>
<p><u>Note:</u> if jconsole fails to connect and show
you <a href="docfiles/remote-connection-failed.jpg">this screen</a>
you have probably misspelled some of the properties on jconsole
command line, or you didn't start jconsole from the
scandir example root directory where our <code>truststore</code>
and <code>keystore</code> files are located. This article - <a
href="http://blogs.sun.com/roller/page/jmxetc?entry=troubleshooting_connection_problems_in_jconsole"
title="Troubleshooting connection problems in JConsole"
>Troubleshooting connection problems in JConsole</a> - may help
you figure out what is going wrong.
</p>
<hr>
</ul>
<h3>Writing a programmatic client to connect to the secure agent</h3>
<ul>
<p>
In this section we will show the steps involved in writing
a programmatic client that will connect to our secure agent.
</p>
<p>The <a
href="dist/javadoc/com/sun/jmx/examples/scandir/ScanDirClient.html"
title="The ScanDirClient class is a very short example of secure programmatic client"
>ScanDirClient</a> is an example class that shows how a
programmatic client can connect to a secured <i>scandir</i> application.
This class contains a <code>main</code> method which creates and
configures a <code>JMXConnector</code> client to connect with
the secured <i>scandir</i> agent.
</p>
<p>The secure client differs only from a non secure client in
so far as it needs to use SSL RMI Factories and credentials to
connect to the secure agent. The steps required mainly involve:
<ul>
<li>Creating an empty environment map:
<pre>
// Create an environment map to hold connection properties
// like credentials etc... We will later pass this map
// to the JMX Connector.
//
System.out.println("\nInitialize the environment map");
final Map&lt;String,Object> env = new HashMap&lt;String,Object>();
</pre>
</li>
<li>Putting the client's credentials in that map:
<i>(here the client will log in as <b>guest</b>)</i>
<pre>
// Provide the credentials required by the server
// to successfully perform user authentication
//
final String[] credentials = new String[] { "guest" , "guestpasswd" };
env.put("jmx.remote.credentials", credentials);
</pre>
</li>
<li>Providing an <code>SslRMIClientSocketFactory</code> to interact
with the secure RMI Registry:
<pre>
// Provide the SSL/TLS-based RMI Client Socket Factory required
// by the JNDI/RMI Registry Service Provider to communicate with
// the SSL/TLS-protected RMI Registry
//
env.put("com.sun.jndi.rmi.factory.socket",
new SslRMIClientSocketFactory());
</pre>
</li>
<li>Creating a JMXConnector and connecting with the
secure server:
<pre>
// Create the RMI connector client and
// connect it to the secure RMI connector server.
// args[0] is the server's host - localhost
// args[1] is the secure server port - 4545
//
System.out.println("\nCreate the RMI connector client and " +
"connect it to the RMI connector server");
final JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://"+args[0]+":"+args[1]+
"/jmxrmi");
final JMXConnector jmxc = JMXConnectorFactory.connect(url, env);
</pre>
</li>
</ul>
<p>For this to work, we also need to start the <code>ScanDirClient</code>
with the appropriate system properties that will point to our
<code>keystore</code> and <code>truststore</code>. To start the secure
client, go to the <i>scandir</i> example root directory and type
the following command:
<p><code>ant jar<br>
java
&nbsp;-Djava.util.logging.config.file=logging.properties
&nbsp;-Djavax.net.ssl.keyStore=keystore
&nbsp;-Djavax.net.ssl.keyStorePassword=password
&nbsp;-Djavax.net.ssl.trustStore=truststore
&nbsp;-Djavax.net.ssl.trustStorePassword=trustword
&nbsp;-classpath&nbsp;dist/jmx-scandir.jar
&nbsp;com.sun.jmx.examples.scandir.ScanDirClient&nbsp;localhost&nbsp;4545
</code></p>
</p>
<p>You should be seeing this trace:
<center><table width="90%" border="0" bgcolor="#eeeeee">
<tr><td>
<pre>
Initialize the environment map
Create the RMI connector client and connect it to the RMI connector server
Connecting to: service:jmx:rmi:///jndi/rmi://localhost:4545/jmxrmi
Get the MBeanServerConnection
Get ScanDirConfigMXBean from ScanManagerMXBean
Get 'Configuration' attribute on ScanDirConfigMXBean
Configuration:
&lt;ScanManager xmlns="jmx:com.sun.jmx.examples.scandir.config" name="testconfig">
&lt;InitialResultLogConfig>
&lt;LogFileMaxRecords>2048&lt;/LogFileMaxRecords>
&lt;LogFileName>build/scandir.log&lt;/LogFileName>
&lt;MemoryMaxRecords>128&lt;/MemoryMaxRecords>
&lt;/InitialResultLogConfig>
&lt;DirectoryScannerList>
&lt;DirectoryScanner name="scan-build">
&lt;Actions>NOTIFY LOGRESULT&lt;/Actions>
&lt;ExcludeFiles/>
&lt;IncludeFiles>
&lt;FileFilter>
&lt;FilePattern>.*\.class&lt;/FilePattern>
&lt;SizeExceedsMaxBytes>4096&lt;/SizeExceedsMaxBytes>
&lt;/FileFilter>
&lt;/IncludeFiles>
&lt;RootDirectory>build&lt;/RootDirectory>
&lt;/DirectoryScanner>
&lt;/DirectoryScannerList>
&lt;/ScanManager>
Invoke 'close' on ScanManagerMXBean
Got expected security exception: java.lang.SecurityException: Access denied!
Invalid access level for requested MBeanServer operation.
Close the connection to the server
Bye! Bye!
</pre>
</td></tr></table></center>
<p>If the <code>ScanDirClient</code> fails to connect with
the secure agent, then this article - <a
href="http://blogs.sun.com/roller/page/jmxetc?entry=troubleshooting_connection_problems_in_jconsole"
title="Troubleshooting connection problems in JConsole"
>Troubleshooting connection problems in JConsole</a> - may help
you figure out what is going wrong. Indeed the connection steps
performed by the <code>ScanDirClient</code> are very similar to
those performed by <code>jconsole</code>, and the problems you
could encounter are identical. Just remember that
<code>jconsole</code> needs the extra <code>-J</code> flag to pass
system properties to the VM, which is not needed with regular
<code>java</code> launcher invocations.
</p>
</ul>
<h2><a name="h2-Conclusion">Conclusion</a></h2>
<ul>
<p>
In this document, we have presented an advanced
JMX example, and shown how to run a secure
JMX agent in a production environment.
We have also shown how to connect to such a
secure agent with both jconsole and a programmatic
client. We have also discuss various JMX
design-patterns and best practices.
Readers who would wish to learn more about JMX, and
Monitoring and Management of the JVM, are invited
to follow the links given in reference below.
</p>
</ul>
<h2><a name="h2-References">References</a></h2>
<ol>
<li><a href="http://java.sun.com/products/JavaManagement/best-practices.html"
>JMX Best Practices</a>: This document describes best practices that
have been identified for modeling using the JMX API. </li>
<li><a href="http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html"
>Monitoring and Management Using JMX</a>: How to enable, configure, and
connect to the JVM JMX agent.</li>
<li><a name="JConsole"><a
href="http://java.sun.com/javase/6/docs/technotes/guides/management/jconsole.html"
>Using JConsole</a>: JConsole is a JMX-Compliant monitoring tool which allows
you to interact graphically with your own MBeans.
</li>
<li><a href="http://java.sun.com/javase/6/docs/technotes/guides/management/"
>Monitoring and Management for the Java Platform</a>: The Java Platform
Standard Edition (Java SE) 6 provides comprehensive monitoring and
management support for the Java platform. </li>
<li><a href="http://java.sun.com/products/JavaManagement/community/jmx_blogs.html"
>List of JMX-related Blogs</a>: This page provides links to the
different web logs written by members of the Sun team working on the
JMX API.</li>
<li><a
href="http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#HowSSLWorks"
title="The JSSE Reference Guide"
>Java<sup>TM</sup> Secure Socket Extension (JSSE) Reference Guide</a>:
comprehensive documentation about the Java<sup>TM</sup> Secure Socket
Extension (JSSE)
</li>
<li><a href="http://java.sun.com/javase/6/docs/"
>Java SE 6 Documentation Index</a>: This document covers the
Java<sup>TM</sup> Platform, Standard Edition 6 JDK.</li>
</ol>
<p>
<hr>
<p>
</body>
</html>