Table of Contents
OpenJMX provides some extension to the JMX specification; nevertheless, full compatibility of MBeans is guaranteed. This ensures that user MBeans will work in the same way on every implementation of the JMX specification; when used with the OpenJMX implementation, however, some additional benefit is provided.
When MBeans are coded as standard MBeans, the JMX Agent is responsible of the creation of the metadata
information for them. Conversely, when coded as dynamic MBeans, the programmer is responsible of the
creation of such metadata information exposed to the JMX Agent through the
public MBeanInfo getMBeanInfo
(); method of the
DynamicMBean interface.
Although the JMX Agent can retrieve information about attributes, operations, constructors and notifications, for standard MBeans it cannot retrieve user information such as attribute description, operation description and parameter names and descriptions, and so on.
These information are important for the user of a management application, that can immediately understand what an operation parameter is for just reading the description associated with that parameter. Same happens with attributes.
OpenJMX offers the possibility of customize descriptions and parameter's names for attributes, operations, constructors and notifications of standard MBeans. This customization is achieved by implementing a class that follows some lexical patterns, in a way very similar to what happens to standard MBeans and the Java interface that represent their management interface to the JMX Agent.
The MBean programmer should write a class that has the same full qualified name of the MBean class, ends with "MBeanDescription" and implement the openjmx.MBeanDescription interface or extends the openjmx.MBeanDescriptionAdaptor class.
For example if you have an MBean whose class is my.package.MyService, then you will have a management interface defined by the my.package.MyServiceMBean Java interface, and you may add a class named my.package.MyServiceMBeanDescription that implements the openjmx.MBeanDescription interface and that specifies descriptions and parameter's names for the MBean.
Example 2.1. Specifying description for standard MBeans
public interface MyServiceMBean { public void start(); public void setStatus(int status); } public class MyService implements MyServiceMBean { public MyService(String type) {...} public void start() {...} public void stop() {...} public void setStatus(int status) {...} } public class MyServiceMBeanDescription extends MBeanDescriptionAdaptor { public String getConstructorDescription(Constructor ctor) { // Only one constructor return "Creates a new instance of my personal service"; } public String getConstructorParameterName(Constructor ctor, int index) { // Constructor has only one parameter return "type"; } public String getConstructorParameterDescription(Constructor ctor, int index) { // Constructor has only one parameter return "The type of the service. Valid values are 'VOLATILE' or 'PERMANENT'."; } public String getAttributeDescription(String attribute) { // There is only one attribue, 'Status' return "The status of the service. Can be set to ON=1, OFF=0"; } public String getOperationDescription(Method operation) { String name = operation.getName(); if (name.equals("start")) { return "Starts the service. After the service is started its status is ON"; } else if (name.equals("stop")) { return "Stops the service. After the service is stopped its status is OFF"; } else { return super.getOperationDescription(operation); } } }
Sometimes it is very tedious calling the MBeanServer.invoke
method, since the invocation is very verbose and since
you have to deal with exception handling that is not directly related to the method you want to call
on the MBean.
Take a look at this example, taken from OpenJMX implementation of the persistence for MBeans:
Example 2.2. Typical MBeanServer.invoke invocation
public interface PersisterMBean { public void store(Object data) throws MBeanException, RuntimeOperationsException, InstanceNotFoundException; ... } public abstract class Persister implements PersisterMBean {} public class MBeanPersister extends Persister { private MBeanServer m_server; private ObjectName m_name; public MBeanPersister(MBeanServer server, ObjectName name) { m_server = server; m_name = name; } public void store(Object data) throws MBeanException, RuntimeOperationsException, InstanceNotFoundException { try { m_server.invoke(m_name, "store", new Object[] {data}, new String[] {"java.lang.Object"}); } catch (ReflectionException x) { throw new MBeanException(x.getTargetException()); } } }
As you can see, the store implementation uses the MBeanServer.invoke
call that is error-prone, and forces you to catch and rethrow exceptions that
are not related to the invocation of the store method.
However, thanks to the dynamic proxy API introduced in J2SE version 1.3, it is possible to avoid all the problems we mentioned above, using the openjmx.util.StandardMBeanProxy class. Take a look at the example below and compare the store implementation to see how useful this utility class is:
Example 2.3. Proxy MBeanServer invocation
public class MBeanPersister extends Persister { private MBeanServer m_server; private ObjectName m_name; private PersisterMBean m_proxy; public MBeanPersister(MBeanServer server, ObjectName name) { m_server = server; m_name = name; m_proxy = (PersisterMBean)StandardMBeanProxy.create(PersisterMBean.class, server, name); } public void store(Object data) throws MBeanException, RuntimeOperationsException, InstanceNotFoundException { m_proxy.store(data); } }
As you can see, the invocation is now type-safe so that possible errors are caught by the compiler, and there is no need of extra exception handling.
The StandardMBeanProxy class works with standard MBeans, since you have to specify the management MBean interface
to the StandardMBeanProxy.create
and cast the returned object to the same
interface. You should not call methods inherited from java.lang.Object, but only methods
belonging to the management MBean interface.
OpenJMX has a flexible logging system that allows you to tune the logging priority and to redirect OpenJMX internal logging to other logging systems such as Log4J.
The OpenJMX logging system has six logging priorities; from the lowest priority to the highest they are:
trace
debug
info
warn
error
fatal
For example, you can start the JVM with this command to have OpenJMX log at a debug level:
java -Dopenjmx.log.priority=debug MyMainClass
The fatal level is never used by OpenJMX.
OpenJMX default logging is done on the console via System.out, but can be redirected to other logging systems using the OpenJMX logging API, or through the broadcaster MBean (see below).
For example, let's assume you want to redirect OpenJMX logging to Log4J. Below is the code needed to do so:
Example 2.4. Logging redirection to Log4J
import org.apache.log4j.PropertyConfigurator; import openjmx.log.*; public class Main { public static void main(String[] args) throws Exception { // Configure Log4J PropertyConfigurator.configureAndWatch("log4j.properties"); // Or use the XML version below // DOMConfigurator.configureAndWatch("log4j.xml"); // Redirect OpenJMX logging to Log4J Log.redirectTo(new Log4JLogger()); // Normal code here MBeanServer server = MBeanServerFactory.createMBeanServer(); ... // Reset redirection, log in the normal way (to console) Log.redirectTo(null); } }
It is also shown how to reset logging redirection to the standard one, that logs on the console.
It is possible to redirect logging to the OpenJMX broadcaster MBean, that will send notifications to registered listeners. The MBeanServer mechanism is used to emit these notifications, so that every listener can register itself along with a filter, to exclude notifications in which it's not interested in. The example below shows the code necessary to redirect the logging system to the broadcaster MBean:
Example 2.5. Logging redirection to the broadcaster MBean
import openjmx.log.*; public class Main { public static void main(String[] args) throws Exception { MBeanServer server = MBeanServerFactory.createMBeanServer(); // Register the broadcaster logger mbean ObjectName name = new ObjectName("Logger:type=broadcaster"); server.createMBean("openjmx.log.LoggerBroadcaster", name, null); // The filter: only errors are logged NotificationFilter filter = new NotificationFilter() { public boolean isNotificationEnabled(Notification notification) { if (notification.getType().equals("openjmx.logger.error")) {return true;} return false; } }; // The listener: logs on System.err instead of System.out NotificationListener listener = new NotificationListener() { public void handleNotification(Notification notification, Object handback) { System.err.println("[OpenJMX ERROR]: " + notification); } }; // Register the listener along with the filter server.addNotificationListener(name, listener, filter, null); // Starts the redirector LoggerBroadcasterMBean redirector = (LoggerBroadcasterMBean)MBeanProxy.create(LoggerBroadcasterMBean.class, server, name); redirector.start(); ... // Stops the redirector redirector.stop(); } }
It is also shown how to reset logging redirection to the standard one, that logs on the console.