# Services

## The Relation Service example

### Introduction

A full description of the RelationService, what it does and why it does it can be found in the JMX Specifications.

The example java source files can be found in the examples directory under services/relation, they include:

- `RelationServiceExample` (the main class)
- `SimpleBooks`
- `SimpleOwner`
- `SimplePersonalLibrary` (which extends the `javax.mangement.relation.RelationTypeSupport` and provides the definitions of our roles)

### Simple use-case for the example

- 1)Adding Books Use-Case: Main Success Scenario:

A user adds 1 book to his personal library, he already has 3, a check is done and it is determined that he is allowed 4 books, there are no problems and he can add the book.

- 2)Adding Books Use-Case: Alternate Scenarios:

Our user decides he would like to add another book, as he has defined the number of books to be minimum 1 and maximum 4, and he is now trying to add a fifth, he is not allowed to add the extra book..

- 3)Removing Books Use-Case: Main Success Scenario:

A User decides to remove 3 old books from his personal-library. As he has defined the number of books he is allowed as to being between 1 and 4 there are no problems, the books are removed and he can no longer read or write to them, from the RelationService..

- 4)Removing Books Use-Case: Alternate Scenario:

The book owner decides to remove all his books. The relation is invalidated and he can no longer access his records as they have been removed from the RelationService, including his role as Owner..

### Code Usage

Before any relations can be defined the RelationService must be registered in the MBeanServer.

#### Creating the RelationService

    import javax.management.relation.RelationService;

    MBeanServer server = MBeanServerFactory.createMBeanServer();
    String className = "javax.management.relation.RelationService";
    ObjectName objectName = new ObjectName("Relations:name=RelationService");

    // The boolean value is to enable a purge of relations to determine invalid relations when an unregistration occurs of MBeans
    Object[] params = {new Boolean(true)};
    String[] signature = {"boolean"};
    server.createMBean(className, objectName, null, params, signature);

Once we have the RelationService registered we can then create in the server our MBeans that will be playing the roles in our use-case scenarios. This being done we can proceed to adding our RelationType `SimplePersonalLibrary` which must extend `javax.management.relation.RelationTypeSupport`. This class is not registered in the MBeanServer, it is merely a simple way of providing the definitions of our Roles in the RelationService, an example of adding a RelationType in the RelationService follows:

#### Adding a RelationType

    // SimplePersonalLibrary is our RelationTypeSupport class
    String relationTypeName = "my_library";
    SimplePersonalLibrary library = new SimplePersonalLibrary(relationTypeName);

    Object[] params = {library};
    String[] signature = {"javax.management.relation.RelationType"};

    server.invoke(objectName, "addRelationType", params, signature);

Our next step will be to start filling the roles we defined in our support class and adding the MBeans up to the maximum number we defined our SimplePersonalLibrary class. This means registering the MBeans first with MBeanServer. Once registered. we can add them within our Roles...

#### Building Roles

    // building the owner Role
    ArrayList ownerList = new ArrayList();
    ownerList.add(ownerName1);  // can only add owner to an owner role cardinality defined as 1
    Role ownerRole = new Role("owner", ownerList);

    // building the book role
    ArrayList bookList = new ArrayList();
    // we can have between 1 and 4 books more than 4 invalidates out relation and less than 1 invalidates it
    bookList.add(bookName1);
    bookList.add(bookName2);
    bookList.add(bookName3);
    Role bookRole = new Role("books", bookList);

    // add our roles to the RoleList
    RoleList libraryList = new RoleList();
    libraryList.add(ownerRole);
    libraryList.add(bookRole);

    // now create the relation
    Object[] params = {personalLibraryId, libraryTypeName, libraryList};
    String[] signature = {"java.lang.String", "java.lang.String", "javax.management.relation.RoleList"};
    m_server.invoke(m_relationObjectName, "createRelation", params, signature);

We are done a note about the alternate scenarios: Once Role cardinality has been invalidated the relation is removed from the RelationService and can no longer be accessed via the RelationService though any MBeans registered in the MBeanServer can still be accessed individually.

### Using Examples from the JMX Reference Implementation

The RelationService examples which can be downloaded from the JMX website will run in the MX4J implementation. The few changes required are due to the fact that MX4J implements the accessors of MBeans as server.getAttribute(..) and server.setAttribute(...) whereas the JMX implements all as method calls using server.invoke(..)

To be able to use the Examples from the JMX download. A list of the few changes required for the `RelationAgent` follows:

- Remove: import com.sun.jdmk.Trace;
- Remove: Trace.parseTraceProperties();
- Change all calls for
  - getAllRelationTypeNames
  - getRelationServiceName
  - getRelationId
  - getAllRelationIds
  - getReferencedMBeans Note: except where the call comes from an external relation(represented by a subclass of `javax.management.relation.RelationSupport` or a type of `javax.management.relation.Relation`
  - getRelationTypeName Note: same as above
  - getAllRoles Note: same as above
  - setRole Note: same as above
  from server.invoke(...) to server.getAttribute(....) , server.setAttribute(...) depending on whether it sets or gets.

## MBeans

## RMI MBean example

### Introduction

The purpose of this example is to give some guideline on how to expose RMI remote objects also as MBeans.

This will give to server administrators the possibility to manage RMI remote objects in the JMX sense. This means that may be possible to start, stop and restart the service implemented by the remote object; and also checking if it is running, or ask to reload configurations files.

### The example classes

The example java source files can be found in the examples directory under mbeans/rmi, they include:

- `Server` (the class that starts the remote service)
- `Client` (the client class)
- `MyRemoteService` (the remote interface)
- `MyRemoteServiceObjectMBean` (the management interface)
- `MyRemoteServiceObject` (the MBean that implements the remote interface)

### Understanding the example

The remote object is, at the same time an MBean and an RMI remote object.

Clients can see it through 2 interfaces: users of the service see it through the remote interface ( `MyRemoteService`), while management application (such as the HTTP adaptor) see it through the management interface ( `MyRemoteServiceObjectMBean`). The remote interface contains only one method:

public void `sayHello(String name)`

that will execute client's requests. The management interface, conversely, contains 3 methods:

public void `start()` public void `stop()` public boolean `isRunning()`

that will be used by management application such as the HTTP adaptor.

Notice that users of the remote interface cannot invoke methods of the management interface, as well as management application cannot invoke methods of the remote interface.

### The implementation

The purpose of the `Server` class is only to start a JMX Agent by instantiating the MBeanServer, register the service we want to expose, and starting it.

#### The `Server` class

    public class Server
    {
       public static void main(String[] args) throws Exception
       {
          MBeanServer server = MBeanServerFactory.createMBeanServer();

          ObjectName name = new ObjectName("examples:type=remote");
          MyRemoteServiceObject remote = new MyRemoteServiceObject();
          server.registerMBean(remote, name);

          MyRemoteServiceObjectMBean managed = (MyRemoteServiceObjectMBean)MBeanServerInvocationHandler.newProxyInstance(server, name, MyRemoteServiceObjectMBean.class, false);
          managed.start();
       }
    }

The remote object, instance of `MyRemoteServiceObject` class, is worth some more detail. First notice that it extends `java.rmi.server.RemoteServer` and not `java.rmi.server.UnicastRemoteObject`. This is done to avoid that the object is automatically exported to the RMI runtime when creating it, since we want to control the export and unexport via the `start()` and `stop()` methods of the management interface. Second, notice the symmetry of the `start()` and `stop()` methods: `start()` export the object to the RMI runtime and binds it in the naming, while `stop()` unbinds it from the naming and unexport it from the RMI runtime.

#### The `MyRemoteServiceObject` class

    public class MyRemoteServiceObject extends RemoteServer implements MyRemoteService, MyRemoteServiceObjectMBean
    {
       private boolean m_running;

       public MyRemoteServiceObject() throws RemoteException {}

       public void sayHello(String name) throws RemoteException
       {
          System.out.println("Hello, " + name);
       }

       public void start() throws Exception
       {
          if (!m_running)
          {
             UnicastRemoteObject.exportObject(this);
             InitialContext ctx = new InitialContext();
             ctx.rebind(JNDI_NAME, this);
             m_running = true;
             System.out.println("My remote service started successfully");
          }
       }

       public void stop() throws Exception
       {
          if (m_running)
          {
             InitialContext ctx = new InitialContext();
             ctx.unbind(JNDI_NAME);
             UnicastRemoteObject.unexportObject(this, false);
             m_running = false;
             System.out.println("My remote service stopped successfully");
          }
       }

       public boolean isRunning()
       {
          return m_running;
       }
    }

Thus, will be possible to start the service via a management application and let it available to users; and will be possible to stop it, maybe changing some configuration file (not in this simple example, but you got the picture), and restarting it, WITHOUT shutting down other services that may have been started by the same JMX Agent.

The implementation of the `sayHello(String name)` method is straightforward, as well as the `isRunning()` one that, accessible from management applications, returns if the service is running or not.

### Compiling the example files

The above classes must be compiled using javac, and the `MyRemoteServiceObject` class must be compiled using rmic.

Let's suppose you unpack the MX4J distribution in the mx4j- ver directory; from this directory you issue these commands:

C:\mx4j- ver>javac -classpath lib\mx4j-jmx.jar examples\mbeans\rmi\*.java C:\mx4j- ver>rmic mx4j.examples.mbeans.rmi.MyRemoteServiceObject

### Running the example

To run the example, three consoles are needed:

- One for the rmiregistry
- One for the server
- One for the client

For the rmiregistry, you need to have in the classpath the RMI stub of the `MyRemoteServiceObject` class you just compiled with rmic. Then you can start it by typing the following command:

C:\mx4j- ver>set classpath=. C:\mx4j- ver>rmiregistry

For the server, you need all the compiled classes (apart for the `Client` class), mx4j-jmx.jar (the JMX implementation), and a suitable jndi.properties file (there is a default one shipped with this example) in the classpath. Then you can start the server with the following command:

C:\mx4j- ver>java -cp .;examples;lib\mx4j-jmx.jar mx4j.examples.mbeans.rmi.Server

For the client, you need the `Client` class, the remote interface and the RMI stub, and a suitable jndi.properties file (there is a default one shipped with this example). Then you can start the client with the following command:

C:\mx4j- ver>java -cp .;examples mx4j.examples.mbeans.rmi.Client

## Tools

## Using XDoclet

### Introduction

MX4J provides a way to generate source code of MBean interfaces and descriptions using some custom javadoc comments in source code. This source generation is done with the help of XDoclet. It is recommended to look at the example included in the distribution for getting started with this feature.

### Preparing Ant

In order to generate the source code, XDoclet must be copied into the directory where all libraries are. This is usually the lib subdirectory of the project which will be referred as the Ant variable `${lib}` from now on. A path must be defined for the Ant task. An example is given below:

#### Defining the xdoclet classpath and Ant task.

    <path id="xdoclet">
       <pathelement location="${lib.dir}/xjavadoc.jar"/>
       <pathelement location="${lib.dir}/xdoclet.jar"/>
       <pathelement location="${lib.dir}/xdoclet-jmx-module.jar"/>
       <pathelement location="${lib.dir}/xdoclet-mx4j-module.jar"/>
       <pathelement location="${lib}/log4j.jar"/>
       <pathelement location="${ANT_HOME}/lib/ant.jar"/>
       <pathelement location="${build}"/>
    </path>
    <taskdef name="jmxdoclet" classname="xdoclet.jmx.JMXDocletTask">
       <classpath refid="xdoclet"/>
    </taskdef>

Let's suppose now that the Ant compilation target is called `compilation`. The easiest way to generate the source code of the MBean interface and description is to make the `compilation` depending on the target `generateJMX` defined by:

    <target name="generate-jmx" depends="init">
       <!-- Generate the MBean interfaces and descriptions -->
       <jmxdoclet sourcepath="${src}" destdir="${src}" classpathref="xdoclet" force="yes">
          <fileset dir="${src}">
             <include name="com/xtremejava/webos/**" />
          </fileset>
          <!-- Create the {0}MBean interface for the MBean -->
          <mbeaninterface/>
          <!-- Create the MX4J specific description adaptor class for the MBean -->
          <mx4jdescription />
          <!-- Generate the mlet file -->
          <mlet destinationFile="mbeans.mlet" destdir="conf"/>
       </jmxdoclet>
    </target>

### Basic usage

A few javadoc tags can be used in order to specify how to generate the interface and the description of your MBean.

### List of Javadoc tags

Javadoc Tag Name | Meaning
@jmx:mbean | Tag used in order to define the name of the MBean and its description. Must be defined at class level.
@jmx:managed-constructor | Tag used in order to define the name of the constructor and its description. Must be defined at constructor level.
@jmx:managed-constructor-parameter | Tag used in order to define the name of a constructor attribute, it's index, and its description. Must be defined at constructor level.
@jmx:managed-operation | Tag used in order to define the name of an operation and its description. Must be defined at method level.
@jmx:managed-operation-parameter | Tag used in order to define the name of an operation attribute, it's index, and its description. Must be defined at method level.
@jmx:managed-attribute | Tag used in order to define the description of an attribute. Must be defined at method level. Note: the getter description has priority over the setter description.
@jmx:mlet-entry | Tag used in order to generate the mlets file. All classes having this tags will have the result merge in one file. Must be defined at class level.

## FilePersister example

### Introduction

This example will cover the usage of the FilePersister with MLets. It will give a good overview of how to use MLets into the bargain :-). What we will do is create two MBeans the one extends FilePersister, so that it can load and store Objects. The other is a standard MBean which will ask the FilePersisterMBean to store it, The interesting thing about this example is that the two mbeans are loaded by two differnet MLets from two different jar files. One note of importance any object to be stored must implement the interface Serializable. Let us begin.

### Writing the code

We are going to write an MBean which extends FilePersister and pass any calls made to it to its parent class.

#### MBeanOne implementation

    public class MBeanOne extends FilePersister implements Serializable
    {
       public MBeanOne(String location, String name) throws MBeanException
           {
          super(location, name);
           }

       public void store(Object mbean)throws MBeanException, InstanceNotFoundException
       {
          store(mbean);
       }

       public Object load()throws MBeanException, RuntimeOperationsException, InstanceNotFoundException
       {
          return load();
       }
    }

Now to do the mbean that will ask the MBeanOne to store it.

#### MBeanTwo implementation

    public class MBeanTwo implements MBeanTwoMBean, Serializable
    {
       // constructor... see example
       //we are now going to invoke MBeanOne through the MBeanServer
       public void storeIt(MBeanServer server, ObjectName name)
       {
          server.invoke(name, "store", new Object[]{this}, new String[]{"java.lang.Object"});
       }

       public Object loadIt(MBeanServer server, ObjectName name)
       {
          Object me = null;
          try
          {
             me = (MBeanTwo)server.invoke(name, "load", new Object[0], new String[0]);
          }
          catch (Exception ex)
          {
             ex.printStackTrace();
          }
          return me;
       }
    }

### The Agent MLet implementation

The "main" class will now create and register two MLets, each MLet will load its own MBean from separate jar files

#### The FilePersisterAgent

    // Create the MBeanServer
    // Build the ObjectNames for the MLets
    // Register the MLets
    MLet mlet1 = new MLet();
    m_server.registerMBean(mlet1, mName1);
    mlet1.addURL(jarOneUrl);

    MLet mlet2 = new MLet();
    m_server.registerMBean(mlet2, mName2);
    mlet2.addURL(jarTwoUrl);

    // We now have access to the MBeans, so instantiate them
    m_server.createMBean(mbeanClassName, mbeanObjectName, mLetObjectName, params, signature);

    // As above but the other mbean is now registered using the othe MLet object name as the third parameter
    m_server.createMBean(.....);

    // Now invoke the storage of one MBean by the other
    m_server.invoke(mbeanName2, "storeIt", new Object[] {m_server, mbeanName1},
                 new String[]{"javax.management.MBeanServer", "javax.management.ObjectName"});

    // Now load it
    Object a = m_server.invoke(mbeanName2, "loadIt", new Object[] {m_server, mbeanName1},
               new String[]{"javax.management.MBeanServer", "javax.management.ObjectName"});

    // And finally a test to see that the objects are equal
    if(a.getClass().getName() == mbeanClass2) System.out.println("Objects are equal and the same");

### Steps for Running the Example

Once you have the files and compiled them you will need to build the jars holding the mbeans start a command prompt (windows) cd to the examples/classes directory and type in the following commands:

    jar cvf one.jar examples/tools/persister/MBeanOne.class
    // and then
    jar cvf two.jar examples/tools/persister/MBeanTwo.class examples/tools/persister/MBeanTwoMBean.class

Now delete the MBeanOne.class and MBeanTwo.class (we do not want them in the classpath :-). Once this is done add four application parameters

- 1) path to jar one.jar eg:file:C:/dev/mx4j/one.jar
- 2) path to jar two.jar eg: file:C:/dev/two.jar
- 3) path where to store the file eg: C:/temp
- 4) name of the file eg: test.ser

You are now ready to start the agent! Have fun !!