Chapter 6. MX4J Tools

Table of Contents

XML Configuration Loader
Loading and starting MBeans using XML files
Dynamic MBeans
AbstractDynamicMBean base class for DynamicMBean implementation
Naming MBeans
The NamingService MBean
The CosNamingService MBean
Mailer MBean
Introduction
Configuration
Keyword expanding
Jython MBean
Introduction
Configuration
Built-in functions

XML Configuration Loader

Loading and starting MBeans using XML files

The javax.management.loading.MLet class provides a way to load and instantiate MBeans from a configuration file called MLet file.
The MLet file has however a limited syntax, since it can only instantiate and register MBeans into an MBeanServer: no method invocation is possible, no MBean's unregistration, no possibility to use factories to create objects and so on. Furthermore, the MLet file format is not XML.

MX4J's mx4j.tools.config.ConfigurationLoader defines an XML configuration file format where it is possible to specify creation of objects, their registration into an MBeanServer, their unregistration, the invocation of methods, both via MBeanServer (JMX invocations) and via normal Java invocation.

Let's see a simple example that registers MX4J's HTTP adaptor into an MBeanServer.
First, we need a small class that creates the MBeanServer and the ConfigurationLoader:

Example 6.1. Main class for configuration loading

            
public class Startup
{
   public static void main(final String[] args) throws Exception
   {
      // MX4J's logging redirection to Apache's Commons Logging
//    mx4j.log.Log.redirectTo(new CommonsLogger());

      // Create the MBeanServer
      MBeanServer server = MBeanServerFactory.createMBeanServer();

      // Create the ConfigurationLoader
      ConfigurationLoader loader = new ConfigurationLoader();

      // Register the configuration loader into the MBeanServer
      ObjectName name = ObjectName.getInstance(":service=configuration");
      server.registerMBean(loader, name);

      // Tell the configuration loader the XML configuration file
      Reader reader = new BufferedReader(new FileReader(args[0]));
      loader.startup(reader);
      reader.close();
   }
}
            
            

The class simply creates an MBeanServer, creates the ConfigurationLoader and tells the configuration loader to read an XML file from the path passed as argument.
The XML file can be the following:

Example 6.2. XML file for configuration of MX4J's HttpAdaptor

            
<?xml version="1.0" encoding="ISO-8859-1"?>
<configuration>
   <startup>
      <create classname="mx4j.tools.adaptor.http.HttpAdaptor" objectname="connectors:type=http" loadername="null">
         <arg type="int">9090</arg>
         <arg type="string">localhost</arg>
      </create>
      <create classname="mx4j.tools.adaptor.http.XSLTProcessor" objectname="connectors:type=http,processor=xslt" loadername="null"/>
      <call objectname="connectors:type=http" attribute="ProcessorNameString">
         <arg type="string">connectors:type=http,processor=xslt</arg>
      </call>
      <call objectname="connectors:type=http" operation="start"/>
   </startup>

   <shutdown>
      <call objectname="connectors:type=http" operation="stop"/>
   </shutdown>
</configuration>
            
            

The first thing to notice is that the configuration file is splitted in 2 sections, namely startup and shutdown. We will see later how the shutdown section comes into play.

Let's examine the startup section.
It contains the creation of the mx4j.tools.adaptor.http.HttpAdaptor MBean (passing 2 arguments to its constructor) and of the mx4j.tools.adaptor.http.XSLTProcessor MBean via the create element.
These MBeans are registered in the MBeanServer that also manages the ConfigurationLoader MBean.
Both these 2 operations are possible also with the MLet file.
What is different is the possibility to set JMX attributes and invoke JMX operations on the MBeans just registered. This is achieved with the call element: first the XSLT processor is set onto the HTTP adaptor, then the HTTP adaptor itself is started.

Let's see now a more complex example: how to register and start a JSR 160 connector server.

Example 6.3. XML file for configuration of a JSR 160 connector server

            
<?xml version="1.0" encoding="ISO-8859-1"?>
<configuration>
   <startup>
      <create classname="mx4j.tools.naming.NamingService" objectname="naming:type=rmiregistry">
         <arg type="int">1099</arg>
      </create>
      <call operation="start" objectname="naming:type=rmiregistry" />

      <object objectid="rmi">
         <call classname="javax.management.remote.JMXConnectorServerFactory" method="newJMXConnectorServer">
            <arg type="javax.management.remote.JMXServiceURL">
               <new classname="javax.management.remote.JMXServiceURL">
                  <arg type="string">service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmx</arg>
               </new>
            </arg>
            <arg type="java.util.Map" />
            <arg type="javax.management.MBeanServer" />
         </call>
      </object>
      <register objectname="connectors:type=rmi,protocol=jrmp">
         <arg type="object" refobjectid="rmi" />
      </register>
      <call method="start" refobjectid="rmi" />
   </startup>

   <shutdown>
      <call method="stop" refobjectid="rmi" />
      <call operation="stop" objectname="naming:type=rmiregistry" />
      <unregister objectname="connectors:type=rmi,protocol=jrmp" />
      <unregister objectname="naming:type=rmiregistry" />
   </shutdown>
</configuration>
            
            

Note the usage of the object element. It is possible to assign an ID (in the example is "rmi") to objects that gets created in the XML file, and refer to them afterwards using the refobjectid attribute.
Note also the syntax to invoke the static call to the JMXConnectorServerFactory, and the syntax to invoke non-JMX operations when calling "start" on the "rmi" object.
Objects can be created by using the new element, and constructor arguments specified with the arg element. Note how null arguments are passed to the JMXConnectorServerFactory (the second and third argument of type Map and MBeanServer).

Let's now explain the meaning of the shutdown section.
If the startup section defines how the MBeans should be created, registered and put into operation (for example by calling "start" operations on them), the shutdown section defines how the MBeans should be stopped from operating and eventually unregistered.

It remains the problem of how to make the configuration loader invoke the shutdown section.

This is done by specifying a port as attribute of the configuration element; the configuration loader will start a server socket on that port, so that from another JVM will be possible to connect to that socket and invoke the shutdown command.
For security reasons, the server socket will listen on the loopback interface of the host, so that only JVMs started on the same host can invoke the shutdown section.

Below, an example of a class that invokes the shutdown section and a sample XML file that specifies the port for the server socket.

Example 6.4. Simple XML file that specifies the command port

            
<?xml version="1.0" encoding="ISO-8859-1"?>
<configuration port="9876">
   <startup>
      <create classname="mx4j.tools.naming.NamingService" objectname="naming:type=rmiregistry">
         <arg type="int">1099</arg>
      </create>
      <call operation="start" objectname="naming:type=rmiregistry" />
   </startup>

   <shutdown>
      <call operation="stop" objectname="naming:type=rmiregistry" />
      <unregister objectname="naming:type=rmiregistry" />
   </shutdown>
</configuration>
            
            

Example 6.5. Simple Java class that invokes "shutdown" to execute the shutdown section

            
public class Shutdown
{
   public static void main(String[] args) throws Exception
   {
      Socket socket = new Socket("127.0.0.1", 9876);
      socket.getOutputStream().write("shutdown".getBytes());
      socket.close();
   }
}
            
            

The XML configuration DTD is the following:

Example 6.6. XML configuration DTD

            
<!ELEMENT configuration (startup?, shutdown?)>
<!ATTLIST configuration port CDATA #IMPLIED>

<!ELEMENT startup (call*, create*, object*, register*)>

<!ELEMENT shutdown (call*, unregister*)>

<!ELEMENT call (arg*)>
<!ATTLIST call classname CDATA #IMPLIED>
<!ATTLIST call objectname CDATA #IMPLIED>
<!ATTLIST call refobjectid CDATA #IMPLIED>
<!ATTLIST call method CDATA #IMPLIED>
<!ATTLIST call operation CDATA #IMPLIED>
<!ATTLIST call attribute CDATA #IMPLIED>

<!ELEMENT create (arg*)>
<!ATTLIST create classname CDATA #REQUIRED>
<!ATTLIST create objectname CDATA #IMPLIED>
<!ATTLIST create loadername CDATA #IMPLIED>

<!ELEMENT object (call | new)>
<!ATTLIST object objectid CDATA #REQUIRED>

<!ELEMENT new (arg*)>
<!ATTLIST new classname CDATA #REQUIRED>

<!ELEMENT register (arg*)>
<!ATTLIST register objectname CDATA #IMPLIED>

<!ELEMENT unregister EMPTY>
<!ATTLIST unregister objectname CDATA #REQUIRED>

<!ELEMENT arg (call | new | #PCDATA)>
<!ATTLIST arg type CDATA #REQUIRED>