Chapter 9. MX4J Developer's guide
Prev |  |

## Chapter 9. MX4J Developer's guide

## Introduction

This chapter aims to give some suggestion to developers of MX4J, in order to have some common base to start from. It gives also some indication on how to create and distribute a new MX4J release, and the general architecture of the MX4J implementation.

### Coding conventions

The base to start from are the coding conventions for Java, see here.
We require as additional convention the one-line one-brace style:

Example 9.1. One-line one-brace style

    public class Main { // bad
    	private String name;
    	public Main(String n) { // bad
    		if (n != null) { // bad
    			name = n;
    		}
    	}
    }

    public class Main
    { // good
    	private String name;
    	public Main(String n)
    	{ // good
    		if (n != null)
    		{ // good
    			name = n;
    		}
    	}
    }

### Using logging

It is good practice to insert logging statements in the code, to help debugging and to record information about what the program is doing.

MX4J has a built-in logging system based on the `mx4j.log.Logger` class, whose usage is very similar to the `Category` class of the Log4J project.

Class `Logger` has six methods that logs at a different priority; from the lowest priority to the highest they are:

- `public void trace`(...);

- `public void debug`(...);

- `public void info`(...);

- `public void warn`(...);

- `public void error`(...);

- `public void fatal`(...);

They all have two overloaded version, one that takes one argument of type `Java.lang.Object`, and one that takes two arguments of type `Java.lang.Object` and `java.lang.Throwable`.

Class `Logger` has another useful method that returns the priority enabled for that instance of `Logger`:

- `public boolean isEnabledFor`(...);

Using correctly this method will save the run-time cost associated with the concatenation of `String` objects:

Example 9.2. Saving run-time cost of `String` concatenation

    Logger logger = ...;
    if (logger.isEnabledFor(Logger.TRACE))
    {
    	logger.trace("Result is: " + result + " on item: " + item + " for process: " + process);
    }

Saving the time associated with the creation of temporary objects used only for the logging statement is mandatory when the priority is trace, debug or info, to avoid unnecessary drop of performance.

Another useful way to avoid creation of temporary `String` objects is to use the `StringBuffer` class, following this example:

Example 9.3. Saving run-time cost of `String` concatenation with `StringBuffer`

    Logger logger = ...;
    if (logger.isEnabledFor(Logger.TRACE))
    {
    	StringBuffer message = new StringBuffer("Result is: ").append(result).append(" on item: ").append(" for process: ").append(process);
    	logger.trace(message);
    }

This version, when the logging is performed because the priority is enabled for the logger instance, is more efficient than the previous example that used `String` concatenation.

How do you obtain an instance of the `Logger` class ? You must use the `mx4j.log.Log` class, this way:

Example 9.4. Retrieving a Logger instance

    Logger logger = Log.getLogger("MyCategory");

Every logger instance is associated with a category, i.e. a string that normally represent a correlated group of logging requests.

#### Choosing the right priority

Choosing the right priority is important, and here there are few guidelines:

- Use `Logger.trace` to log execution flow. Always surround a log with trace priority with a call to `Logger.isEnabledFor`.

- Use `Logger.debug` to log variables values. Always surround a log with debug priority with a call to `Logger.isEnabledFor`.

- Use `Logger.info` to log information that can be of interest for the user. For every public method there should be at least one log with info priority. Always surround a log with info priority with a call to `Logger.isEnabledFor`.

- Use `Logger.warn` to log recoverable errors that in normal situations should not happen. The warn priority is the default priority for MX4J.

- Use `Logger.error` to log exceptions. Typically, log with error priority are inside catch blocks, just before rethrowing:

  Example 9.5. Logs with error priority

      Logger logger = ...;
      try
      {
      	thisMethodThrows();
      }
      catch (Exception x)
      {
      	logger.error("Exception happened: ", x);
      	throw x;
      }

- Use `Logger.fatal`(...); to log fatal errors that will force the JVM to terminate. Typically log with fatal priority are inside catch blocks, just before calling `System.exit`:

  Example 9.6. Logs with fatal priority

      Logger logger = ...;
      try
      {
      	Class.forName("java.lang.Object");
      }
      catch (ClassNotFoundException x)
      {
      	logger.fatal("Can't find core classes", x);
      	System.exit(1);
      }

  However, it is mandatory that in the MX4J code `System.exit` is never called.

### Writing documentation

The documentation that comes with MX4J is written using DocBook. It is very easy to use DocBook, and an on-line manual is available here.

Generally, the layout is defined in the file index.xml. All other files are DocBook sections belonging to a chapter, also defined in index.xml.

### Releasing MX4J

The following steps are required for a new release of MX4J:

- Fresh checkout from CVS. Do NOT update, checkout to a new directory the whole CVS
- Add to lib directory all the optional libraries
- Build the code and run the test suite. All tests must pass
- Build the distribution kit
- Check that the distribution kit contains all the jars, the sources and licensing information (also for third party software used)
- Tag CVS with the suitable release tag
- Upload the new kit to SourceForge, creating a new release version
- Update the Change Notes tracker in SourceForge
- Submit a news in SourceForge
- Submit a news in FreshMeat
- Submit a news in JavaLobby
- Submit a news in TheServerSide
- Submit a news in jmx-forum@java.sun.com
- Submit a news in comp.lang.java.announce

### MX4J Implementation Architecture

The primary implementation class of the MX4J JMX Agent is `mx4j.server.MX4JMBeanServer`.

The MBeanServer implementation class accomplishes these roles:

- Returns information about the Agent
- It is a repository for MBeans
- Introspect the MBeans to determine their type and compliance
- It acts as invoker, on behalf of the user, of operations on the registered MBeans

The information about the JMX Agent are returned by several methods such as, for example, `getDefaultDomain()`, and are implemented directly in the `mx4j.server.MX4JMBeanServer` class.

The registered MBeans are stored into a repository.
The MBeanServer implementation delegates to implementations of the `mx4j.server.MBeanRepository` interface the repository task; therefore the MBeanServer implementation acts as a factory for MBeanRepository instances, but the implementation is delegated to MBeanRepository instances.
It is possible to specify custom MBeanRepository implementations by specifying the full qualified name of the implementation class as value of the system property "mx4j.mbeanserver.repository".

When an MBean is registered several checks must be made to determine the MBean's type, if it is a compliant MBean or not and to retrieve its MBeanInfo information.
The MBeanServer implementation delegates this task to the `mx4j.server.MBeanIntrospector` class.
Objects of this class are first asked to fully introspect the given MBean; after the MBeanIntrospector has gathered all information about the MBean into an instance of `mx4j.server.MBeanMetaData` class, the MBeanServer implementation asks the MBeanIntrospector to check the compliance of the MBean.
If the MBean is a standard one, the MBeanIntrospector must create the MBeanInfo information for it using introspection on the MBean's management interface, and create the MBean invoker for it, see below.
The role of the MBeanIntrospector is thus to check the MBean compliance, to create the MBeanInfo information for standard MBeans and to act as a factory for MBean invokers.

The MBeanServer implementation acts as an invoker, on behalf of the user, of operations on the registered MBeans.
The architecture is interceptor-based, that is whenever you call from a client an MBeanServer method that will end up to call the MBean instance, the call is dispatched to the interceptor chain and eventually to the MBean instance.
The interceptors are configurable via the MBeanServer Interceptor Configurator MBean, that is an MBean of class `mx4j.server.MBeanServerInterceptorConfigurator` registered under the "JMImplementation" domain.
When the call is about to arrive to the MBean instance, the last interceptor dispatches the call depending on the MBean type: if the MBean is a dynamic MBean, the call is dispatched directly using the methods of the `DynamicMBean` interface; if the MBean is a standard MBean, then an MBean invoker is delegated to invoke the operation on the MBean instance.
MBean invokers are implementations of the `mx4j.server.MBeanInvoker` interface.
There are 2 default MBean invoker implementations: the first is based on reflection to invoke the MBean instance, the second is based on an on-the-fly generated class that invokes the MBean directly. This on-the-fly generated class is created at registration time; its bytecode is built at runtime using the Byte Code Engineering Library.
Both versions make use of TernaryTree as fast caches for MBean information, so that the invocations on standard MBeans are really FAST.
Early performance benchmarks reveal that the on-the-fly generated version is 2 times faster than the one that use reflection.

For further information, see the Javadocs of the cited classes.

Prev | Home |
Chapter 8. FAQ | Up |