Contents | Previous | Next | Java Management Extensions (JMX) Technology Tutorial |
This chapter introduces the concepts of standard and dynamic management beans (MBeans) and also shows how to use Java Management Extensions (JMX) technology to perform operations on MBeans, both locally and remotely.
This example demonstrates standard and dynamic MBeans only.
As seen in Chapter 2, "Essentials of the JMX API", a standard MBean is one that statically defines its management interface through the names of the methods it contains. A dynamic MBean implements a specific Java interface and reveals its attributes and operations at run time.
The JMX technology defines a connector based on RMI. The RMI connector supports the Java Remote Method Protocol (JRMP) transport, and optionally, the Internet Inter-Object Request Broker (ORB) Protocol (IIOP) transport. This connector allows you to connect to an MBean in an MBean server from a remote location, and perform operations on it, exactly as if the operations were being performed locally.
The purpose of this example is to demonstrate the implementation of a standard MBean and a dynamic MBean. It also shows how to perform operations on them, both locally, and remotely through an RMI connection between a server and a remote client.
When you run this example:
SimpleStandard
and a
SimpleDynamic
MBean in the local MBean
serverThe RMI connector example is contained in the
directory work_dir/jmx_examples/Basic
.
The following sections analyze each of the classes used in the basic MBean example, and explain how they perform the operations described in the preceding section.
Due to its size, the Server.java
class is shown in several code
excerpts.
public class Server { public static void main(String[] args) { try { MBeanServer mbs = MBeanServerFactory.createMBeanServer(); waitForEnterPressed(); String domain = mbs.getDefaultDomain(); waitForEnterPressed(); String mbeanClassName = "SimpleStandard"; String mbeanObjectNameStr = domain + ":type=" + mbeanClassName + ",name=1"; ObjectName mbeanObjectName = createSimpleMBean(mbs, mbeanClassName, mbeanObjectNameStr); waitForEnterPressed(); printMBeanInfo(mbs, mbeanObjectName, mbeanClassName); waitForEnterPressed(); manageSimpleMBean(mbs, mbeanObjectName, mbeanClassName); waitForEnterPressed(); mbeanClassName = "SimpleDynamic"; mbeanObjectNameStr = domain + ":type=" + mbeanClassName + ",name=1"; mbeanObjectName = createSimpleMBean(mbs, mbeanClassName, mbeanObjectNameStr); waitForEnterPressed(); printMBeanInfo(mbs, mbeanObjectName, mbeanClassName); waitForEnterPressed(); manageSimpleMBean(mbs, mbeanObjectName, mbeanClassName); waitForEnterPressed(); [...]
Examining this class, you can see that the following occurs:
Firstly, the Server.java
class creates a new MBean server called
mbs
by calling the createMBeanServer()
method of the MBeanServerFactory
class.
Then, the default domain in which the MBean server
is registered is obtained with a call to the getDefaultDomain()
method of the MBeanServer
interface. The domain is identified by
the string domain
.
The MBean class named SimpleStandard
is also identified by a variable, in
this case the string mbeanClassName
.
SimpleStandard
is the name of the Java
class for the Java object of which this MBean is an instance. The
SimpleStandard.java
object is examined
in Section "SimpleStandard.java".
Another variable, the string mbeanObjectNameStr
, is defined as the combination of
the domain, plus the following key=value pairs:
type
, which in this case is the
mbeanClassName
.name
, to differentiate this MBean
from other MBeans of the same type that might be created
subsequently. In this case the name number is 1
.The purpose of mbeanObjectNameStr
is to give the MBean a
human-readable identifier.
A call to createSimpleMBean() creates and registers the SimpleStandard MBean in the local MBean server, with the given object name.
The operations printMBeanInfo()
, and manageSimpleMBean()
are then performed on the
SimpleStandard
MBean. Like createSimpleMBean()
, these methods are defined later
in the Server.java
code, and are shown
in CODE EXAMPLE 3-4 and
CODE EXAMPLE 3-5.
In code that is not shown here, a second MBean of
the type SimpleDynamic
is created and
registered in the MBean server in exactly the same way as the
SimpleStandard
MBean. As the name
suggests, this MBean is an instance of the SimpleDynamic
Java object, which is examined in
Section "SimpleDynamic.java".
[...] JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/server"); JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); cs.start(); waitForEnterPressed(); cs.stop(); [...]
In CODE EXAMPLE 3-2, an RMI
connector server is created so that operations can be performed on
the MBeans remotely. A call to the class JMXServiceURL
creates a new service URL called
url
, which serves as an address for the
connector server. In this example, the service URL is given in
JNDI form, rather than in encoded form (see the API documentation for the
javax.management.remote.rmi
package for
an explanation of JNDI form). This service URL defines the
following:
rmi
.9999
on the
local host, and the server address will be registered under the
name server
. The port 9999
specified in the example is arbitrary; you can
use any available port.An RMI connector server named cs
is created by calling the constructor
JMXConnectorServerFactory
, with the
service URL url
, a null
environment map, and the MBean server
mbs
as parameters. The connector server
cs
is launched by calling the
start()
method of JMXConnectorServer
, whereupon RMIConnectorServer
exports the RMI object
server
to the RMI registry. The
connection will remain open until the Enter key is pressed, as
instructed by the simple method waitForEnterPressed
, that is defined later in the
Server
code.
[...] private static ObjectName createSimpleMBean(MBeanServer mbs, String mbeanClassName, String mbeanObjectNameStr) { echo("\n>>> Create the " + mbeanClassName + " MBean within the MBeanServer"); echo("ObjectName = " + mbeanObjectNameStr); try { ObjectName mbeanObjectName = ObjectName.getInstance(mbeanObjectNameStr); mbs.createMBean(mbeanClassName, mbeanObjectName); return mbeanObjectName; } catch (Exception e) { echo( "!!! Could not create the " + mbeanClassName + " MBean !!!"); e.printStackTrace(); echo("\nEXITING...\n"); System.exit(1); } return null; } [...]
CODE EXAMPLE 3-3 shows
the definition of the createSimpleMBean()
method. In this method, the
MBean instance with the object name mbeanObjectNameStr
is passed to the getInstance()
method of the ObjectName
interface to create a new object name for
registering the MBean inside the MBean server. The resulting object
name instance is named mbeanObjectName
.
A call to the MBeanServer
method
createMBean()
then instantiates an MBean
defined by the combination of the Java object identified by
mbeanClassName
and the MBean instance
mbeanObjectName
and registers this MBean
in the MBean server mbs
.
[...] private static void printMBeanInfo(MBeanServer mbs, ObjectName mbeanObjectName, String mbeanClassName) { MBeanInfo info = null; try { info = mbs.getMBeanInfo(mbeanObjectName); } catch (Exception e) { echo( "!!! Could not get MBeanInfo object for " + mbeanClassName +" !!!"); e.printStackTrace(); return; } MBeanAttributeInfo[] attrInfo = info.getAttributes(); if (attrInfo.length > 0) { for (int i = 0; i < attrInfo.length; i++) { echo(" ** NAME: " + attrInfo[i].getName()); echo(" DESCR: " + attrInfo[i].getDescription()); echo(" TYPE: " + attrInfo[i].getType() + "READ: "+ attrInfo[i].isReadable() + "WRITE: "+ attrInfo[i].isWritable()); } } else echo(" ** No attributes **"); [...]
In CODE EXAMPLE 3-4 we see
the definition of the method printMBeanInfo()
. The printMBeanInfo()
method calls the MBeanServer
method getMBeanInfo()
to obtain details of the attributes
and operations that are exposed by the MBean mbeanObjectName
. MBeanAttributeInfo
defines the following methods,
each of which is called in turn to obtain information about the
mbeanObjectName
MBean’s
attributes:
getName
, to obtain the
attribute’s name.getDescription
, to obtain the human
readable description of the attribute.getType
, to obtain the class name of
the attribute.isReadable
, to determine whether or
not the attribute is readable.isWritable
, to determine whether or
not the attribute is writable.In code that is not shown here, calls are made to
obtain information about the mbeanObjectName
MBean’s constructors,
operations and notifications:
MBeanConstructorInfo
, to obtain
information about the MBean’s Java class.MBeanOperationInfo
, to learn what
operations the MBean performs, and what parameters it takes.MBeanNotificationInfo
, to find out
what notifications the MBean sends when its operations are
performed.[...] private static void manageSimpleMBean(MBeanServer mbs, ObjectName mbeanObjectName, String mbeanClassName) { try { printSimpleAttributes(mbs, mbeanObjectName); Attribute stateAttribute = new Attribute("State", "new state"); mbs.setAttribute(mbeanObjectName, stateAttribute); printSimpleAttributes(mbs, mbeanObjectName); echo("\n Invoking reset operation..."); mbs.invoke(mbeanObjectName, "reset", null, null); printSimpleAttributes(mbs, mbeanObjectName); } catch (Exception e) { e.printStackTrace(); } } private static void printSimpleAttributes( MBeanServer mbs, ObjectName mbeanObjectName) { try { String State = (String) mbs.getAttribute(mbeanObjectName, "State"); Integer NbChanges = (Integer) mbs.getAttribute(mbeanObjectName, "NbChanges"); } catch (Exception e) { echo( "!!! Could not read attributes !!!"); e.printStackTrace(); } } [...]
CODE EXAMPLE 3-5 shows a method for managing a simple MBean.
The manageSimpleMBean()
method first of all calls the printSimpleAttributes()
method that is also defined
by Server
. The printSimpleAttributes()
method obtains an MBean
attribute called state
from the MBean
mbeanObjectName
, as well as another
MBean attribute called NbChanges
. Both
of these attributes are defined in the SimpleStandard
class, shown in Section "SimpleStandard.java".
The manageSimpleMBean()
method then defines an attribute called stateAttribute
, which is an instance of the
Attribute
class. The stateAttribute
attribute associates a value of
new state
with the existing attribute
state
, defined by SimpleStandard
. A call to the MBeanServer
method setAttribute()
then sets the mbeanObjectName
MBean’s state to the new state
defined by stateAttribute
.
Finally, a call to the MBeanServer
method invoke()
invokes the mbeanObjectName
MBean’s reset
operation. The reset
operation is defined in the SimpleStandard
class.
The SimpleStandardMBean.java
class is shown in CODE EXAMPLE 3-1.
public interface SimpleStandardMBean { public String getState(); public void setState(String s); public int getNbChanges(); public void reset(); }
The SimpleStandardMBean.java
class is a straightforward
JMX specification management interface for the MBean SimpleStandard
. This interface exposes the four
operations defined by SimpleStandard
for
management through a JMX agent.
The SimpleStandard.java
class is shown in CODE EXAMPLE 3-1.
public class SimpleStandard extends NotificationBroadcasterSupport implements SimpleStandardMBean { public String getState() { return state; } public void setState(String s) { state = s; nbChanges++; } public int getNbChanges() { return nbChanges; } public void reset() { AttributeChangeNotification acn = new AttributeChangeNotification(this, 0, 0, "NbChanges reset", "NbChanges", "Integer", new Integer(nbChanges), new Integer(0)); state = "initial state"; nbChanges = 0; nbResets++; sendNotification(acn); } public int getNbResets() { return nbResets; } public MBeanNotificationInfo[] getNotificationInfo() { return new MBeanNotificationInfo[] { new MBeanNotificationInfo( new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE }, AttributeChangeNotification.class.getName(), "This notification is emitted when the reset() method is called.") }; } private String state = "initial state"; private int nbChanges = 0; private int nbResets = 0; }
The SimpleStandard
class defines a straightforward JMX specification standard
MBean.
The SimpleStandard
MBean exposes operations and attributes for management by
implementing the corresponding SimpleStandardMBean
interface, shown in Section "SimpleStandardMBean.java".
The simple operations exposed by this MBean are as follows:
The notification emitted by the reset operation is
an instance of the class AttributeChangeNotification
, which collects
information about the number of changes carried out on the
State
attribute before calling reset.
The content of the notification sent is defined by the MBeanNotificationInfo
instance.
The SimpleDynamic
class
is shown in CODE EXAMPLE 3-1.
public class SimpleDynamic extends NotificationBroadcasterSupport implements DynamicMBean { public SimpleDynamic() { buildDynamicMBeanInfo(); } [...]
The SimpleDynamic
dynamic MBean shows how to expose attributes and operations for
management at runtime, by implementing the DynamicMBean
interface. It starts by defining a
method, buildDynamicMBeanInfo()
, for
obtaining information for the MBean dynamically. The buildDynamicMBeanInfo()
method builds the
MBeanInfo
for the dynamic MBean.
The rest of the code of SimpleDynamic
corresponds to the implementation of
the DynamicMBean
interface. The
attributes, operations and notifications exposed are identical to
those exposed by the SimpleStandard
MBean.
The ClientListener.java
class is shown in CODE EXAMPLE 3-1.
public class ClientListener implements NotificationListener { public void handleNotification(Notification notification, Object handback) { System.out.println("\nReceived notification: " + notification); } }
The ClientListener
class implements a straightforward JMX specification notification
listener.
The handleNotification()
method of the NotificationListener
interface is called upon
reception of a notification, and prints out a message to confirm
that a notification has been received.
The Client.java
class
is shown in CODE EXAMPLE 3-1.
public class Client { public static void main(String[] args) { try { // Create an RMI connector client // JMXServiceURL url = new JMXServiceURL( "service:jmx:rmi:///jndi/rmi://localhost:9999/server"); JMXConnector jmxc = JMXConnectorFactory.connect(url, null); ClientListener listener = new ClientListener(); MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); waitForEnterPressed(); // Get domains from MBeanServer // String domains[] = mbsc.getDomains(); for (int i = 0; i < domains.length; i++) { System.out.println("Domain[" + i + "] = " + domains[i]); } waitForEnterPressed(); String domain = mbsc.getDefaultDomain(); // Create SimpleStandard MBean ObjectName mbeanName = new ObjectName(domain +":type=SimpleStandard,name=2"); mbsc.createMBean("SimpleStandard", stdMBeanName, null, null); waitForEnterPressed(); // Create SimpleDynamic MBean ObjectName dynMBeanName = new ObjectName(domain +":type=SimpleDynamic,name=2"); echo("\nCreate SimpleDynamic MBean..."); mbsc.createMBean("SimpleDynamic", dynMBeanName, null, null); waitForEnterPressed(); // Get MBean count echo("\nMBean count = " + mbsc.getMBeanCount()); // Query MBean names echo("\nQuery MBeanServer MBeans:"); Set names = mbsc.queryNames(null, null); for (Iterator i = names.iterator(); i.hasNext(); ) { echo( "ObjectName = " + (ObjectName) i.next()); } waitForEnterPressed(); mbsc.setAttribute(stdMBeanName, new Attribute("State", "changed state")); SimpleStandardMBean proxy = JMX.newMBeanProxy( mbsc, stdMBeanName, SimpleStandardMBean.class, true); echo("\nState = " + proxy.getState()); ClientListener listener = new ClientListener(); mbsc.addNotificationListener(stdMBeanName, listener, null, null); mbsc.invoke(stdMBeanName, "reset", null, null); mbsc.removeNotificationListener(stdMBeanName, listener); mbsc.unregisterMBean(stdMBeanName); [...] jmxc.close(); } catch (Exception e) { e.printStackTrace(); } } } [...]
The Client.java
class
creates an RMI connector client that is configured to connect to
the RMI connector server created by Server.java
.
As you can see, Client.java
defines the same service URL
url
as that defined by Server.java
. This allows the connector client to
retrieve the RMI connector server stub named server
from the RMI registry running on port
9999
of the local host, and to connect
to the RMI connector server.
With the RMI registry thus identified, the
connector client can be created. The connector client, jmxc
, is an instance of the interface JMXConnector
, created by the connect()
method of JMXConnectorFactory
. The connect()
method is passed the parameters
url
and a null
environment map when it is called.
The Client also creates an instance of
ClientListener
, to listen for
notifications, as shown in Section "ClientListener.java".
An instance of a JMX specification MBeanServerConnection
, named mbsc
, is then created by calling the getMBeanServerConnection()
method of the
JMXConnector
instance jmxc
.
The connector client is now connected to the MBean
server created by Server.java
, and can
register MBeans and perform operations on them with the connection
remaining completely transparent to both ends.
The client creates and registers the SimpleStandard
MBean and the SimpleDynamic MBean in
the MBean server with a call to the createMBean()
method of MBeanServerConnection
, and performs the operations
defined by SimpleStandard
and
SimpleDynamic
as if they were local JMX
specification MBean operations.
MBean proxies allow you to access an MBean through
a Java interface, allowing you to make calls on the proxy rather
than having to write lengthy code to access a remote MBean. An
MBean proxy for SimpleStandardMBean
is
created here by calling the method newMBeanProxy()
in the javax.management.JMX
class, passing it the
MBean’s MBeanServerConnection
,
object name, the class name of the MBean interface and true, to
signify that the proxy must behave as a NotificationBroadcaster
. You can make proxies for
MXBeans in exactly the same way as for standard MBeans, by simply
calling newMXBeanProxy()
instead of
newMBeanProxy()
.
The code for the different operations performed on
SimpleDynamic
is not shown here, because
the operations are the same as those performed on SimpleStandard
.
Finally, the client unregisters the SimpleStandard
MBean and closes the connection. The
final removeNotificationListener
is
optional, as listeners registered by a remote client are removed
when that client is closed.
Having examined the example classes, you can now
run the example. To run the example, follow the steps below, or see
the README
file:
$
javac *.java
9999
of the local host.
The RMI registry will be used by the Server
to register the RMI connector stub.
$
rmiregistry 9999 &
Server
class.
$
java -classpath . Server
You will see confirmation of the creation of the
MBean server and the creation of the SimpleStandard
MBean in the MBean server. You will
then be prompted to press the Enter key to obtain information
about, and then to perform operations on, the SimpleStandard
MBean.
Once the operations on the SimpleStandard
have completed, the process will be
repeated for the SimpleDynamic
MBean.
Once both the MBeans have been created and their
operations performed, you see the creation of an RMI connector
server, to allow operations to be performed on the MBeans from the
remote Client
.
Client
class in another
terminal window.
$
java -classpath . Client
You will see confirmation of the
creation of the RMI connector client and of the connection with the
connector server. You will also be informed of the domain name, and
the creation and registration of SimpleStandard
and SimpleDynamic MBeans. The client
will then perform operations on SimpleStandard
and SimpleDynamic MBeans, before
unregistering them.