# Extensions

MX4J provides some extension to the JMX specification, some transparent with respect to MBeans or applications portability across JMX implementations, and some that instead ties your MBeans or applications to the MX4J implementation.

## Standard MBean descriptions

This extension is totally transparent with respect to MBeans portability across JMX implementations.

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.

MX4J 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 `mx4j.MBeanDescription` interface or extends the `mx4j.MBeanDescriptionAdapter` 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 `mx4j.MBeanDescription` interface and that specifies descriptions and parameter's names for the MBean (see example below).

The whole mechanism of generating the management interface and the description for standard MBean can be automated using XDoclet (see the section about XDoclet for further details).

When the MyService MBean is used in another JMX implementation, that implementation will not care about the MBeanDescription class, and thus will ignore the additional information you provided. Your MBean will work normally, but the other implementation's MBeanServer will not be able to provide description information to connectors and adaptors. That's why this extension is transparent: it will not cause your MBeans to stop working in another JMX implementation.

### 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 MBeanDescriptionAdapter
          {
             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);
                }
             }
          }

## 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:

### 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:

### 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.

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