Chapter 2. Extensions

Table of Contents

Introduction
Standard MBean descriptions
Proxy for MBeanServer invocations on standard MBeans
Internal logging redirection

Introduction

Simone Bordet

Revision History
Revision $Revision: 1.8 $$Date: 2002/01/23 17:19:19 $

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.

Standard MBean descriptions

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);
		}
	}
}
		
		

Proxy for MBeanServer invocations on standard MBeans

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.

Internal logging redirection

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

The default level is warn, and it can be set to a different value just by setting the system property "openjmx.log.priority" to one of the values above.

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.