| <!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 the Java Documentation</a>] |
| [<a href="#h2-Overview">Overview of the <i>scandir</i> Example</a>] |
| [<a href="#h2-API-Doc">API Documentation and Sources</a>] |
| [<a href="#h2-Patterns">Patterns, Best Practices, and Common Pitfalls</a>] |
| [<a href="#h2-Testing">Testing the <i>scandir</i> Example</a>] |
| [<a href="#h2-Running">Running the <i>scandir</i> Example</a>] |
| [<a href="#h2-Playing">Playing with JConsole</a>] |
| [<a href="#h2-Turning">Turning the example into a Secure JMX Application</a>] |
| [<a href="#h2-Connecting">Connecting to the Secure JMX 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<String,DirectoryScannerMXBean> 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<String,ObjectName></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<String,DirectoryScannerConfig> 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<Notification> notifQueue = |
| new LinkedBlockingQueue<Notification>(); |
| |
| 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 -Djava.util.logging.config.file=logging.properties |
| -Dscandir.config.file=src\etc\testconfig.xml |
| -jar 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 |
| -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</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 |
| -J-Djava.util.logging.config.file=logging.properties |
| -J-Djavax.net.ssl.keyStore=keystore |
| -J-Djavax.net.ssl.keyStorePassword=password |
| -J-Djavax.net.ssl.trustStore=truststore |
| -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<String,Object> env = new HashMap<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 |
| -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 |
| -classpath dist/jmx-scandir.jar |
| com.sun.jmx.examples.scandir.ScanDirClient localhost 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: |
| |
| <ScanManager xmlns="jmx:com.sun.jmx.examples.scandir.config" name="testconfig"> |
| <InitialResultLogConfig> |
| <LogFileMaxRecords>2048</LogFileMaxRecords> |
| <LogFileName>build/scandir.log</LogFileName> |
| <MemoryMaxRecords>128</MemoryMaxRecords> |
| </InitialResultLogConfig> |
| <DirectoryScannerList> |
| <DirectoryScanner name="scan-build"> |
| <Actions>NOTIFY LOGRESULT</Actions> |
| <ExcludeFiles/> |
| <IncludeFiles> |
| <FileFilter> |
| <FilePattern>.*\.class</FilePattern> |
| <SizeExceedsMaxBytes>4096</SizeExceedsMaxBytes> |
| </FileFilter> |
| </IncludeFiles> |
| <RootDirectory>build</RootDirectory> |
| </DirectoryScanner> |
| </DirectoryScannerList> |
| </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> |