Interacting with the MX4J implementation

The MX4J implementation exposes some internal functionality via a public API that can be used by MBeans and applications.
However using these API ties the MBean or the application to the MX4J implementation, making them non-portable across other JMX implementations.

Internal logging redirection

MX4J has a flexible logging system that allows you to tune the logging priority and to redirect MX4J internal logging to other logging systems such as Commons Logging or Log4J.

The MX4J 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 "mx4j.log.priority" to one of the values above.

For example, you can start the JVM with this command to have MX4J log at a debug level:

java -Dmx4j.log.priority=debug MyMainClass

The fatal level is never used by MX4J.

MX4J default logging is done on the console via System.out, but can be redirected to other logging systems using the MX4J logging API, or through the broadcaster MBean (see below).

For example, let's assume you want to redirect MX4J logging to Log4J. Below is the code needed to do so:

Example 4.2. Logging redirection to Log4J

               
import org.apache.log4j.PropertyConfigurator;
import mx4j.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 MX4J 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.

The prototype for the new Logger, in the example above Log4JLogger, can be specified either in the code or by setting the system property "mx4j.log.prototype" to the full qualified name of the Logger subclass, for example:

java -Dmx4j.log.prototype=mx4j.log.Log4JLogger MyMainClass

In this case, using system properties, your MBean or application remains portable across JMX implementations.

It is possible to redirect logging to the MX4J 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 4.3. Logging redirection to the broadcaster MBean

               
import mx4j.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("mx4j.log.LoggerBroadcaster", name, null);

      // The filter: only errors are logged
      NotificationFilter filter = new NotificationFilter()
      {
         public boolean isNotificationEnabled(Notification notification)
         {
            if (notification.getType().equals("mx4j.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("[MX4J ERROR]: " + notification);
         }
      };

      // Register the listener along with the filter
      server.addNotificationListener(name, listener, filter, null);

      // Starts the redirector
      LoggerBroadcasterMBean redirector = (LoggerBroadcasterMBean)MBeanServerInvocationHandler.newProxyInstance(server, name, LoggerBroadcasterMBean.class, true);
      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.

Log categories of the MX4J implementation

Various classes in the MX4J implementation log their activities.
Each class logs with a certain category, that organizes logging into a hierarchy of categories, following a dotted scheme introduced by Log4J.
The categories are simply the full qualified names of the classes that log their activities.

MBeanServerInterceptor configuration

The MX4J implementation allows client code to add custom interceptors for the MBeanServer calls that may end up in a call to an MBean instance.

When the MX4J implementation of the MBeanServer is created, a configurator for the MBeanServer-to-MBean interceptor chain is also created, along with a default set of interceptors (that cannot be removed by the client code).
Each one of the default interceptors is also an MBean, so it can be monitored/managed as a normal MBean, and also the configurator is an MBean, registered with ObjectName "JMImplementation:type=MBeanServerInterceptorConfigurator".

The interceptor configurator exposes a management API that can be invoked as usual via the MBeanServer.
The API allows client code to add and remove custom interceptors, even at runtime, to perform additional tasks such as logging, performance timing, redirection and so on.

Writing a custom interceptor is simple, and requires the client code to implement the interface mx4j.server.interceptor.MBeanServerInterceptor or to extend the class mx4j.server.interceptor.DefaultMBeanServerInterceptor.

Once you have written your custom interceptor, you can add it to the interceptor chain using the API provided by the MBeanServerInterceptorConfigurator, via these methods:

  • addInterceptor(MBeanServerInterceptor interceptor)
  • registerInterceptor(MBeanServerInterceptor interceptor, ObjectName name)

See the Javadoc API documentation relative to the class mx4j.server.interceptor.MBeanServerInterceptorConfigurator and its management interface for further details.
Below you can find a simple example of how to use the MBeanServerInterceptorConfigurator API.

Example 4.4. Using the interceptor configurator API

               
public class Main
{
   public static void main(String[] args) throws Exception
   {
      // Create an MBeanServer instance
      // This will also create and configure an instance of the
      // MBeanServerInterceptorConfigurator MBean
      MBeanServer server = MBeanServerFactory.createMBeanServer();

      // The name under which the configurator is registered
      ObjectName name = new ObjectName("JMImplementation:type=MBeanServerInterceptorConfigurator");

      // Create a custom interceptor
      GetMBeanInfoLoggerInterceptor custom = new GetMBeanInfoLoggerInterceptor();

      // Install the custom interceptor
      server.invoke(name, "addInterceptor", new Object[] {custom}, new String[] {MBeanServerInterceptor.class.getName()});

      // Now every call to getMBeanInfo will be logged
      MBeanInfo info = server.getMBeanInfo(name);
   }

   public static class GetMBeanInfoLoggerInterceptor extends DefaultMBeanServerInterceptor
   {
      public MBeanInfo getMBeanInfo(MBeanMetaData metadata)
      {
         // Use whatever logging system is better for you...
         System.out.println("Call to getMBeanInfo !");
         return super.getMBeanInfo(metadata);
      }
   }
}