Tuesday, May 24, 2011

Introduction to JMX

JMX is a simple, powerful and effective way to expose the various configurable attributes of a application over HTTP.

It is Widely used for divulging the health of your whole system and is accepted as a universal tool for application monitoring

After providing several implementations of JMX, In this Article I will uncover all the basics of JMX and would also mention few tips & tricks useful for production deployments.

What is JMX and Why do we use it?

JMX is a popular pattern to instrument any application and provide the monitoring capabilities to your system.

Just to make it simple think of scenarios where you need to know the response times of various layers in your systems, so that at any point of time you can decide upon the improvement areas.

There are many other examples and use cases in which JMX fits perfectly, like some of some of the popular ones and which i also like are: -
1. Monitoring DB connections and increasing/ decreasing the connection limits.
2. Activating and de-activating of components. same as OSGi
3. Modifying the attributes of any component and that too at runtime
4. etc...

JMX is a Scalable and true pluggable Architecture and it is the only standard pattern which can help in exposing any application and that too over HTTP.

Though other protocols like SNMP is also supported but exposing applications over HTTP becomes more important in case of production, where you have firewall restricting the access.

JMX Architecture




In the above image you would see that whole JMX Architecture is divided into 4 different levels: -

Instrumentation Level - At this level you will find all your Resources (termed as MBeans) which need to be exposed to the client

Agent level - Basically a virtual server which keep tracks of all resources defined at the instrumentaiton level. It also exposes various Agent Services like MLet, Monitoring, Timer, Realtion (we will discuss about all these services shortly)

Connectors - Defines the protocol which will be used by external applications to retreive the information from the Agents

Distributed level Services - External application or management interfaces (mx4j, JConsole) which is used by the end user to communicate with the resources with the help of Connecters. Clients can also build there proprietry applications but they should confirm to the JMX Specifications.

Components Overview - Instrumentation Level

The instrumentation level is further catagorized into the following sets: -

Mbeans: -

1. Standard Mbean
2. Dynamic Mbean
3. Model Mbean
4. Open Mbean

Notification Model: -

1. Listeners
2. Broadcaster/ Emitter
3. Filter

Lets understand them one by one: -

Components Overview - Instrumentation - MBeans - Standard Mbean

Standard Mbeans are the simplest Mbeans and very easy to design and expose.
There management interface defines the various attributes, operations which need to be exposed to the end users

When to use it: -
1. Management structures are pretty Straightforward.
2. Managed data is well defined in advance and unlikely to change often.

Example: -

1. Define a Management Interface, which states the attributes and operations which will be exposed and managed.

public interface StandardBeanMBean {
 
 public void setName(String name);
 
 public String getName();
}

2. Now define A Simple bean which implements the management interface and defines the action which needs to be taken for the operation and attributes define by management interface

public class StandardBean implements StandardBeanMBean{

 private String name;

 public String getName() {
  System.out.println("Returning name = "+name);
  return name;
 }

 public void setName(String name) {
  this.name=name;
  System.out.println("name have been set to = "+name);
  
 }

}

3. Next is simple Main Class which registers the MBean with the MBean Server.

public class RunSBSample {

 /**
  * @param args
  */
 public static void main(String[] args) {
  new RunSBSample().registerBean();
 }

 public void registerBean() {
  try {
   StandardBeanMBean mBean = new StandardBean();
                        //Sun provides a default implementation of MBean Server.
                        //We get the reference of that MBean Server and registers the MBeans
   MBeanServer server = ManagementFactory.getPlatformMBeanServer();
   server.registerMBean(mBean, new ObjectName("mBean:type="
     + mBean.getClass().getCanonicalName()));

   //Wait forever, so that we connect our management Apps :)
   while(true){
    Thread.sleep(99999999);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

4. Now compile the above 3 set of classes and run with following VM arguments: -

-Dcom.sun.management.jmxremote.port=6789 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

Something like this: -

Java -Dcom.sun.management.jmxremote.port=6789 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false RunSBSample

5. Next is run Jconsole (open cmd and type JConsole), which is provided as default management apps and connect to the locally running MBean Server (Later in this article I will also tell you the ways to connect remotely) and navigate to the MBeans tab and expand the tree. refer to the below Snapshots: -



6. Now you will see the attribute section in the left hand side of the console, click on that and you will see the attribute exposed by your MBean - "Name".
7. Rest i will leave it to the users to play around and modify the value of this attribute but just wanted to remind that whatever value user is changing, it is actually modified in the actual Bean Object, which is registered within the server.

Components Overview - Instrumentation - MBeans - Dynamic MBean

Dynamic MBean is a bit different and provide more flexibility in comparison to the Standard MBeans

It expose the management interface at runtime for greatest flexibility.

Important Classes and Interface: -
1. DynamicMBean - Interface which should be implemented by all Dynamic Beans
2. MBeanAttributeInfo[] - Defines the attributes which needs to be exposed to the end users
3. MBeanConstructorInfo[]- Defines the details of the constructor which are used while creating the object of Dynamic MBean
4. MBeanOperationInfo[] - Defines the various operations which are exposed by Dynamic MBean
5. MBeanInfo - provides all nessary information about the Dynamic Mbean

When to use it: -
1. Data structures are likely to evolve often over time
2. Instrumentation must provide more flexibility
3. Need to determine the Structures at runtime
4. Need to separate out the Data structures with the management Structures

Example: -

1. Create a Simple Class, which will be exposed and managed by the DynamicMBean

package jmxSamples.mbeans.dynamicBean;

public class DynamicBeanClass {

 private String name="Sumit";
 private String age="10";
 
 public DynamicBeanClass(){
  
 }
 
 
 public void resetData(){
  setName("");
  setAge("");
 }


 /**
  * @return the name
  */
 public String getName() {
  return name;
 }


 /**
  * @param name the name to set
  */
 public void setName(String name) {
  this.name = name;
 }


 /**
  * @return the age
  */
 public String getAge() {
  return age;
 }


 /**
  * @param age the age to set
  */
 public void setAge(String age) {
  this.age = age;
 }
 
 
}


2. Create Dynamic Bean which extends DynamicMBean and define/ expose the attributes which needs to be managed by the user.

package jmxSamples.mbeans.dynamicBean;

import java.lang.reflect.Constructor;
import java.util.Iterator;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ReflectionException;

public class DynamicBean implements DynamicMBean {

 private String dClassName = DynamicBeanClass.class.getName();
 private String dDescription = "Simple implementation of a dynamic MBean.";

 private MBeanAttributeInfo[] dAttributes = new MBeanAttributeInfo[2];
 private MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
 private MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1];
 private MBeanInfo dMBeanInfo = null;

 private DynamicBeanClass mbeanObj = null;

 /**
  * Constructor
  */
 public DynamicBean() {
  mbeanObj = new DynamicBeanClass();
  buildDynamicBean();
 }

 /**
  * Gets the value of a given the Attribute
  */
 public Object getAttribute(String attribute)
   throws AttributeNotFoundException, MBeanException,
   ReflectionException {
  if(attribute==null){
   System.out.println("attribute is null");
   return null;
  }
  
  if(attribute.equalsIgnoreCase("name")){
   
    return mbeanObj.getName(); 
   
   
  }
  if(attribute.equalsIgnoreCase("age")){
   
    return mbeanObj.getAge();
   
   
  }
  return null;

 }

 /**
  * Gets the value of a given the Attributes
  */
 public AttributeList getAttributes(String[] attributes) {
  if (attributes == null) {
      System.out.println("attr list is null");
      return null;
  }
  AttributeList resultList = new AttributeList();

  // if attributeNames is empty, nothing more to do
  if (attributes.length==0)
      return null;

  // for each attribute, try to set it and add to the result list if successfull
  for (int i=0;i < attributes.length ; i++) {
      String value = attributes[i];
      try {
       resultList.add(new Attribute(value,getAttribute(value)));
   } catch(Exception e) {
   e.printStackTrace();
      }
  }
  return resultList;
     }

 /**
  * Returns an MBean Info
  */
 public MBeanInfo getMBeanInfo() {
  
  return dMBeanInfo;
 }

 /**
  * Executes a operation and return back the results
  */
 public Object invoke(String actionName, Object[] params, String[] signature)
   throws MBeanException, ReflectionException {
  if(actionName==null){
   System.out.println("Action name is null");
   return new Object();
  }
  if(actionName.equalsIgnoreCase("resetData")){
   mbeanObj.resetData();
  }
  return null;
 }

 /**
  * Set the value of a given Attribute
  */
 public void setAttribute(Attribute attribute)
   throws AttributeNotFoundException, InvalidAttributeValueException,
   MBeanException, ReflectionException {
  if(attribute==null){
   System.out.println("attribute is null");
   return;
  }
  
  String name = attribute.getName();
  Object value = attribute.getValue();
  
  if(name==null){
   System.out.println("name is null");
   return; 
  }
  
  if(name.equalsIgnoreCase("name")){
   if(value==null){
    mbeanObj.setName("");
   }
   else{
    mbeanObj.setName(value.toString()); 
   }
   
  }
  if(name.equalsIgnoreCase("age")){
   if(value==null){
    mbeanObj.setAge("");
   }
   else{
    mbeanObj.setAge(value.toString());
   }
   
  }

 }

 /**
  * Set the value of a given Attribute List
  */
 public AttributeList setAttributes(AttributeList attributes) {
  if (attributes == null) {
      System.out.println("attr list is null");
      return attributes;
  }
  AttributeList resultList = new AttributeList();

  // if attributeNames is empty, nothing more to do
  if (attributes.isEmpty())
      return resultList;

  // for each attribute, try to set it and add to the result list if successfull
  for (Iterator i = attributes.iterator(); i.hasNext();) {
      Attribute attr = (Attribute) i.next();
      try {
   setAttribute(attr);
   String name = attr.getName();
   Object value = getAttribute(name); 
   resultList.add(new Attribute(name,value));
      } catch(Exception e) {
   e.printStackTrace();
      }
  }
  return resultList;
     }
 

 /**
  * Private method, used to build the DynamicBean
  * @param mbeanObj
  */
 private void buildDynamicBean() {
  
  dAttributes[0] = new MBeanAttributeInfo("Name", "java.lang.String",
    "Name: Name of the person.", true, true, false);
  dAttributes[1] = new MBeanAttributeInfo(
    "Age",
    "java.lang.String",
    "Age: Age of the Person.",
    true, false, false);

  Constructor[] constructors = this.getClass().getConstructors();
  dConstructors[0] = new MBeanConstructorInfo(
    "SimpleDynamic(): Constructs a DynamicBean object",
    constructors[0]);

  MBeanParameterInfo[] params = null;
  dOperations[0] = new MBeanOperationInfo(
    "resetData",
    "resetData(): reset Name and Age attributes to their initial values",
    params, "void", MBeanOperationInfo.ACTION);

  dMBeanInfo = new MBeanInfo(dClassName, dDescription, dAttributes,
    dConstructors, dOperations, new MBeanNotificationInfo[0]);

 }

}


3. Now define a Main class which registers this dynamic bean with the MBean Server

package jmxSamples.mbeans.dynamicBean;

import java.lang.management.ManagementFactory;

import javax.management.MBeanServer;
import javax.management.ObjectName;

public class RunDBSample {

 /**
  * @param args
  */
 public static void main(String[] args) {
  try{
  DynamicBean mBean = new DynamicBean();
  
  MBeanServer server = ManagementFactory.getPlatformMBeanServer();
  server.registerMBean(mBean, new ObjectName("mBean:type="
    + mBean.getClass().getCanonicalName()));
  
  while(true){
   Thread.sleep(999999);
  }
  }catch(Exception e){
   e.printStackTrace();
  }

 }

}


4. Next perform the #4 and #5 steps same as you did in the previous example and you will see that you are able to expose and modify a attribute of a bean without actually modifying its actual structure. Basically a wrapper on the original Bean, in form of DynamiMBean does the job for you.

Components Overview - Instrumentation - MBeans - Model MBean

Model Mbeans are Generic, configurable MBeans that anyone can use to instrument almost any resource (any Java resource) rapidly.
It is an extension to the Dynamic MBean but with greater flexibility

Important Classes and Interface: -
1. ModelMBeanAttributeInfo[] - Defines the attributes which needs to be exposed to the end users. Accepets Atrributes and operations wrapped in "Descriptor" object
2. ModelMBeanConstructorInfo[] - Defines the details of the constructor which are used while creating the object of Model MBean
3. ModelMBeanOperationInfo[] - Defines the various operations which are exposed by Model MBean
4. ModelMBeanNotificationInfo[] - Describes the various notifications emitted by the Model Bean
5. Descriptor - Defines and state the description (dataType, name default value etc) of each and every operation and atttributes exposed by the Model Beans
6. ModelMBeanInfo - Define a Model MBean at runtime and encapsulates the various openrations, notifications and attributes which needs to be exposed to the end user.

When to use it: -

1. All reasons which inclined to use Dynamic beans
2. Need a generic template for creating manageable Objects at runtime


Example: -

1. Define or assume that there is an Object which needs to be managed and exposed over the network

package jmxSamples.mbeans.modelBean;

public class ModelMBeanSampleClass {

 
 public ModelMBeanSampleClass(){
  
 }
  
 private String name;
 private Integer age;
 /**
  * @return the name
  */
 public String getName() {
  return name;
 }
 /**
  * @param name the name to set
  */
 public void setName(String name) {
  this.name = name;
 }
 /**
  * @return the age
  */
 public Integer getAge() {
  return age;
 }
 /**
  * @param age the age to set
  */
 public void setAge(Integer age) {
  this.age = age;
 }
 
 public void reset(){
  setName("");
  setAge(0);
 }
 
 
}


2. Now define an another class which dynamically gets the reference of the managed Object and registers its operation and attributes with the MBean Server.

package jmxSamples.mbeans.modelBean;

import java.lang.reflect.Constructor;

import javax.management.*;
import javax.management.modelmbean.*;

public class ModelMBean {

 
 private String dDescription = "Simple implementation of a Model Bean.";

 private ModelMBeanAttributeInfo[] dAttributes = new ModelMBeanAttributeInfo[3];
 private ModelMBeanConstructorInfo[] dConstructors = new ModelMBeanConstructorInfo[1];
 private ModelMBeanOperationInfo[] dOperations = new ModelMBeanOperationInfo[5];
 private ModelMBeanNotificationInfo[] dNotifications = null;
 private Descriptor mmbDesc = null;
 private ModelMBeanInfo dMBeanInfo = null;
 private MBeanServer server = null;

 
 
 public void buildModelBeans(MBeanServer server){
  
  try{
  this.server=server;
  
  String domain = server.getDefaultDomain();
  String mbeanName = "ModelSample";
  ObjectName inMbeanObjectName = new ObjectName(domain + ":type=" + mbeanName);
  
  buildDynamicMBeanInfo(server,mbeanName,inMbeanObjectName);
  RequiredModelMBean modelmbean = new RequiredModelMBean(dMBeanInfo);
  modelmbean.setManagedResource(new ModelMBeanSampleClass(), "objectReference");
     server.registerMBean(modelmbean,inMbeanObjectName);
  }catch(Exception e){
   e.printStackTrace();
  }
 }
 
 
 
 private void buildDynamicMBeanInfo(MBeanServer server, String mbeanName, ObjectName inMbeanObjectName)
 {
  try
  {

  
   
   
   
   Class appBean = Class.forName("jmxSamples.mbeans.modelBean.ModelMBeanSampleClass");

   mmbDesc = new DescriptorSupport(new String[] {("name="+inMbeanObjectName),
           "descriptorType=mbean",
           ("displayName="+mbeanName),
           "log=T",
           "logfile=jmxmain.log",
           "currencyTimeLimit=5"});
   
   
   Descriptor nameDesc = new DescriptorSupport();
   nameDesc.setField("name","name");
   nameDesc.setField("descriptorType","attribute");
   nameDesc.setField("displayName","MyName");
   nameDesc.setField("getMethod","getName");
   nameDesc.setField("setMethod","setName");

   dAttributes[0] = new ModelMBeanAttributeInfo("name",
             "java.lang.String",
             "Name: name string.",
             true,
             true,
             false,
             nameDesc);
   
   Descriptor ageDesc = new DescriptorSupport();
   ageDesc.setField("name","age");
   ageDesc.setField("descriptorType", "attribute");
   ageDesc.setField("default", new Integer("0"));
   ageDesc.setField("displayName","MyAge");
   ageDesc.setField("getMethod","getAge");
   ageDesc.setField("setMethod","setAge");
    
   dAttributes[1] = new ModelMBeanAttributeInfo("age",
             "java.lang.Integer",
             "Age: Age of a Person",
             true,
             true,
             false,
             ageDesc);
   
   Descriptor hardValueDesc = new DescriptorSupport();
   hardValueDesc.setField("name","HardValue");
   hardValueDesc.setField("descriptorType","attribute");
   hardValueDesc.setField("value", new Integer("99"));
   hardValueDesc.setField("displayName","HardCodedValue");
   hardValueDesc.setField("currencyTimeLimit","0");
   /* A currencyTimeLimit of 0 means that the value
      cached in the Descriptor is always valid.  So
      when we call getAttribute on this attribute we
      will read this value of 99 out of the
      Descriptor.  */
   
   dAttributes[2] = new ModelMBeanAttributeInfo("HardValue",
             "java.lang.Integer",
             "HardValue: static value in ModelMBeanInfo and not in my Sample Bean",
             true,
             false,
             false,
             hardValueDesc);
   
  /* Constructor[] constructors = appBean.getConstructors();
   
   Descriptor sampleClassdesc = new DescriptorSupport();
    sampleClassdesc.setField("name","ModelMBeanSampleClass");
    sampleClassdesc.setField("descriptorType", "operation");
    sampleClassdesc.setField("role","constructor");
   
   dConstructors[0] = new ModelMBeanConstructorInfo("ModelMBeanSampleClass(): Constructs a ModelBean App",
          constructors[0],
          sampleClassdesc);*/
   
   MBeanParameterInfo[] params = null;
   
   Descriptor resetDesc = new DescriptorSupport();
   resetDesc.setField("name","reset");
   resetDesc.setField("descriptorType","operation");
   resetDesc.setField("class","jmxSamples.mbeans.modelBean.ModelMBeanSampleClass");
   resetDesc.setField("role","operation");
   
   dOperations[0] = new ModelMBeanOperationInfo("reset",
             "reset(): reset Name and Age",
             params ,
             "void",
             MBeanOperationInfo.ACTION,
             resetDesc);
   
   
   Descriptor getNameDesc = new DescriptorSupport(new String[] {"name=getName",
             "descriptorType=operation",
             "class=jmxSamples.mbeans.modelBean.ModelMBeanSampleClass",
             "role=operation"} );
   
   dOperations[1] = new ModelMBeanOperationInfo("getName",
             "get state attribute",
             params ,
             "java.lang.String",
             MBeanOperationInfo.ACTION,
             getNameDesc);
   
   Descriptor setNameDesc = new DescriptorSupport(new String[] {
       "name=setName",
    "descriptorType=operation",
    "class=jmxSamples.mbeans.modelBean.ModelMBeanSampleClass",
    "role=operation"});
   
   MBeanParameterInfo[] setNameParms = new MBeanParameterInfo[] { (new MBeanParameterInfo("newName",
              "java.lang.String",
              "new Name value") )} ;
   
   dOperations[2] = new ModelMBeanOperationInfo("setName",
             "set State attribute",
             setNameParms,
             "void",
             MBeanOperationInfo.ACTION,
             setNameDesc);
   
   Descriptor getAgeDesc = new DescriptorSupport( new String[] {
       "name=getAge",
    "descriptorType=operation",
    "class=jmxSamples.mbeans.modelBean.ModelMBeanSampleClass",
    "role=operation"});
   
   dOperations[3] = new ModelMBeanOperationInfo("getAge",
             "get Age attribute",
             params,
             "java.lang.Integer",
             MBeanOperationInfo.INFO,
             getAgeDesc);
   
   
   Descriptor setAgeDesc = new DescriptorSupport(new String[] {
       "name=setAge",
    "descriptorType=operation",
    "class=jmxSamples.mbeans.modelBean.ModelMBeanSampleClass",
    "role=operation"});
   
   MBeanParameterInfo[] setAgeParms = new MBeanParameterInfo[] { (new MBeanParameterInfo("newAge",
                  "java.lang.Integer",
                  "new value for Number of Changes") )} ;
   
   
   dOperations[4] = new ModelMBeanOperationInfo("setAge",
             "set NbChanges attribute",
             setAgeParms,
             "void",
             MBeanOperationInfo.ACTION,
             setAgeDesc);
   
   
   
   dMBeanInfo = new ModelMBeanInfoSupport("jmxSamples.mbeans.modelBean.ModelMBeanSampleClass",
              dDescription,
              dAttributes,
              dConstructors,
              dOperations,
              dNotifications);
   
   dMBeanInfo.setMBeanDescriptor(mmbDesc);
   
  } catch (Exception e)
      {
   
   e.printStackTrace();
  }
 }

}


3. Same as we did in previous examples, define a main class which registers this Model Bean with the MBean server.

package jmxSamples.mbeans.modelBean;

import java.lang.management.ManagementFactory;

import javax.management.MBeanServer;

public class RunMBSample {

 /**
  * @param args
  */
 public static void main(String[] args) {
  try{
   
   MBeanServer server = ManagementFactory.getPlatformMBeanServer();
   ModelMBean mbean = new ModelMBean();
   mbean.buildModelBeans(server);
   
   while(true){
    Thread.sleep(999999);
   }
   }catch(Exception e){
    e.printStackTrace();
   }

  }

}


4. Next perform the #4 and #5 steps same as you did in the previous example and you will see that you are able to create template like structure which can be used to expose any java object.

Components Overview - Instrumentation - MBeans - Open MBean

Open Mbeans are used to understand and expose new objects (which needs to be managed/ exposed) whenever they are discovered at runtime. They rely on small, predefined set of universal Java types to advertise their functionality.
Biggest benefit is that they share and use management data and operations at runtime without requiring the recompilation.

Important Classes and Interface: -

1. OpenMBeanInfoSupport - Define a Open MBean at runtime and encapsulates the various operations, notifications and attributes which needs to be exposed to the end user.
2. TabularType - Defines a Tabular structure of the data which needs to be exposed.
3. TabularDataSupport - Defines hashmap like data structure to depict the data defined by TabularType.
4. CompositeType - Defines a Composite type which associate the List of OpenTypes with its name and description.
5. CompositeDataSupport - Defines a set of Complex Open Data which represent the CompositeData structures.
6. SimpleType - Defines the Standard data types for the various attributes
7. OpenMBeanParameterInfo - Defines the attributes, contructor, operations for the OpenBean

When to use it: -

1. Where the management application does not necessarily have access to the Java classes of the agent.
2. Java serialization is not supported by the management application and the agent.

Example: -

1. Unlike all other beans, Open Beans define and maps the structure of heterogeneous objects with the Java types and Structure, so here we will define 1 Class which defines the structure of the object which defines the relationship between the "TShirts", "Colors" and its "Sizes".

package jmxSamples.mbeans.openBeans;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.ReflectionException;
import javax.management.RuntimeOperationsException;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
import javax.management.openmbean.OpenMBeanConstructorInfoSupport;
import javax.management.openmbean.OpenMBeanInfoSupport;
import javax.management.openmbean.OpenMBeanOperationInfoSupport;
import javax.management.openmbean.OpenMBeanParameterInfo;
import javax.management.openmbean.OpenMBeanParameterInfoSupport;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularData;
import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;

public class OpenBean implements DynamicMBean {

 // Open MBean Info
 //
 private OpenMBeanInfoSupport OMBInfo;

 // Attributes exposed for management
 //
 private TabularDataSupport tShirts;
 private int nbChanges = 0;

 // Custom open types (and related info) used by this Open MBean class
 //
 private static String[] itemNames = { "model", "color", "size", "price" };
 private static String[] itemDescriptions = { "TShirt's model name",
   "TShirt's color", "TShirt's size", "TShirt's price" };
 private static OpenType[] itemTypes = { SimpleType.STRING,
   SimpleType.STRING, SimpleType.STRING, SimpleType.FLOAT };
 private static CompositeType tShirtType = null;

 // TShirts are indexed according to their model, color and size:
 private static String[] indexNames = { "model", "color", "size" };
 private static TabularType tShirtsType = null;

 // Legal values for TShirt features
 //
 private static String[] legalModels = { "JDMK", "JMX", "JAVA" };
 private static OpenMBeanParameterInfoSupport modelParamInfo;

 private static String[] legalColors = { "black", "white", "red", "green",
   "blue" };
 private static OpenMBeanParameterInfoSupport colorParamInfo;

 private static String[] legalSizes = { "S", "M", "L", "XL", "XXL" };
 private static OpenMBeanParameterInfoSupport sizeParamInfo;

 private static float minPrice = 9.00f;
 private static float maxPrice = 19.99f;
 private static OpenMBeanParameterInfoSupport priceParamInfo;

 static {

  // initializes OpenType instances and ParameterInfo instances
  //
  try {

   // CompositeType instance for a TShirt
   //
   tShirtType = new CompositeType("tShirt", "a TShirt", itemNames,
     itemDescriptions, itemTypes);

   // TabularType instance for the list of TShirts
   //
   tShirtsType = new TabularType("tShirts",
     "List of available TShirts", tShirtType, // row type
     indexNames);

   // Parameter info for the model, color, size and price parameters
   //
   modelParamInfo = new OpenMBeanParameterInfoSupport("model",
     "Valid TShirt model name. Legal models: "
       + Arrays.asList(legalModels).toString(),
     SimpleType.STRING, "JMX", // default model is JMX
     legalModels); // array of legal models
   colorParamInfo = new OpenMBeanParameterInfoSupport("color",
     "Valid product color. Legal colors: "
       + Arrays.asList(legalColors).toString(),
     SimpleType.STRING, "white", // default color is white
     legalColors); // array of legal colors
   sizeParamInfo = new OpenMBeanParameterInfoSupport("size",
     "Valid product size. Legal sizes: "
       + Arrays.asList(legalSizes).toString(),
     SimpleType.STRING, "XL", // default size is XL
     legalSizes); // array of legal sizes
   priceParamInfo = new OpenMBeanParameterInfoSupport("price",
     "Valid product price (ranging from $" + minPrice + " to $"
       + maxPrice + ")", SimpleType.FLOAT, null, // no
                  // default
                  // price
     new Float(minPrice), // Min legal value for price
     new Float(maxPrice)); // Max legal value for price

  } catch (OpenDataException e) {
   // should not happen
   ByteArrayOutputStream bout = new ByteArrayOutputStream();
   PrintWriter pout = new PrintWriter(bout);
   e.printStackTrace(pout);
   pout.flush();
   throw new RuntimeException(bout.toString());
  }

 }

 public OpenBean() {

  try {
   buildMBeanInfo();

   // Create empty TShirts list
   tShirts = new TabularDataSupport(tShirtsType);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 /*  Getters */

 /**
  * Returns a clone of the TShirts list
  */
 public TabularData getTShirts() {
  return (TabularData) tShirts.clone();
 }

 /**
  * Returns the number of time the TShirts list has been updated
  */
 public Integer getNbChanges() {
  return new Integer(nbChanges);
 }

 /**
  * Checks param is a valid value for the specified paramInfo and returns
  * param's value (returns the default value if param is null and paramInfo
  * defines one), or throws an OpenDataException otherwise.
  */
 protected Object checkParam(OpenMBeanParameterInfo paramInfo, Object param)
   throws OpenDataException {

  Object result;

  if (!paramInfo.isValue(param)) {
   throw new OpenDataException("parameter " + paramInfo.getName()
     + "'s value [" + param + "] is not valid");
  } else if (param == null && paramInfo.hasDefaultValue()) {
   result = paramInfo.getDefaultValue();
  } else {
   result = param;
  }

  return result;
 }

 /**
  * Builds and returns a new CompositeData TShirt instance from the specified
  * parameters. If parameter values are not legal according to the
  * OpenMBeanParameterInfo instances for this method, it throws an
  * OpenDataException. If model, color or size are null, it uses the default
  * value provided in the OpenMBeanParameterInfo instances for this method.
  */
 public CompositeData addTShirt(String model, String color, String size,
   Float price) throws OpenDataException {

  // Check parameter values are legal, assign default if necessary, or
  // throws OpenDataException
  //
  model = (String) checkParam(modelParamInfo, model);
  color = (String) checkParam(colorParamInfo, color);
  size = (String) checkParam(sizeParamInfo, size);
  price = (Float) checkParam(priceParamInfo, price);

  Object[] itemValues = { model, color, size, price };
  CompositeData result = new CompositeDataSupport(tShirtType, itemNames,
    itemValues);
  tShirts.put(result);
  nbChanges++;
  return result;
 }

 /**
  * Removes the given tshirt from the list if a tshirt with the same index
  * existed in the list, or does nothing otherwise.
  */
 public void removeTShirt(String model, String color, String size) {
  try{  
  
  Collection index = tShirts.values();
  Iterator itr = index.iterator();
  boolean removed = false;
  while(itr.hasNext()){
   CompositeData data = itr.next();
   if(data.get("model").toString().equalsIgnoreCase(model) && data.get("color").toString().equalsIgnoreCase(color) && data.get("size").toString().equalsIgnoreCase(size)){
    Object removedData[] = tShirts.calculateIndex(data);
    tShirts.remove(removedData);
    removed = true;
    break;
   }
  }
  if (removed) {
   nbChanges++;
  }
  }catch(Exception e){
   e.printStackTrace();
  }
 }

 /*  DynamicMBean interface implementation */

 /**
     *
     */
 public Object getAttribute(String attribute_name)
   throws AttributeNotFoundException, MBeanException,
   ReflectionException {

  if (attribute_name == null) {
   throw new RuntimeOperationsException(new IllegalArgumentException(
     "Attribute name cannot be null"),
     "Cannot call getAttribute with null attribute name");
  }
  if (attribute_name.equals("TShirts")) {
   return getTShirts();
  }
  if (attribute_name.equals("NbChanges")) {
   return getNbChanges();
  }
  throw new AttributeNotFoundException("Cannot find " + attribute_name
    + " attribute ");
 }

 /**
     *
     */
 public void setAttribute(Attribute attribute)
   throws AttributeNotFoundException, InvalidAttributeValueException,
   MBeanException, ReflectionException {

  throw new AttributeNotFoundException(
    "No attribute can be set in this MBean");
 }

 /**
     *
     */
 public AttributeList getAttributes(String[] attributeNames) {

  if (attributeNames == null) {
   throw new RuntimeOperationsException(new IllegalArgumentException(
     "attributeNames[] cannot be null"),
     "Cannot call getAttributes with null attribute names");
  }
  AttributeList resultList = new AttributeList();

  if (attributeNames.length == 0)
   return resultList;

  for (int i = 0; i < attributeNames.length; i++) {
   try {
    Object value = getAttribute((String) attributeNames[i]);
    resultList.add(new Attribute(attributeNames[i], value));
   } catch (Exception e) {
    e.printStackTrace();
   }
  }
  return (resultList);
 }

 /**
     *
     */
 public AttributeList setAttributes(AttributeList attributes) {
  return new AttributeList(); // always empty
 }

 /**
     *
     */
 public Object invoke(String operationName, Object[] params,
   String[] signature) throws MBeanException, ReflectionException {

  if (operationName == null) {
   throw new RuntimeOperationsException(new IllegalArgumentException(
     "Operation name cannot be null"),
     "Cannot call invoke with null operation name");
  }


  if (operationName.equals("removeTShirt")) {

   // check params
   if ((params.length != 3) || !(params[0] instanceof String)) {
    throw new RuntimeOperationsException(
      new IllegalArgumentException(
        "cannot invoke removeTShirt: "
          + "expecting params[i] instanceof CompositeData for i = 0"),
      "Wrong content for array Object[] params to invoke removeTShirt method");
   }
   // invoke removeTShirt
   try {
    removeTShirt((String) params[0], (String) params[1],
      (String) params[2]);
    return "Removed";
   } catch (Exception e) {
    throw new MBeanException(e, "invoking removeTShirt: "
      + e.getClass().getName() + "caught [" + e.getMessage()
      + "]");
   }
  }

  // Add TShirt
  else if (operationName.equals("addTShirt")) {

   // check params
   if ((params.length != 4) || !(params[0] instanceof String)
     || !(params[1] instanceof String)
     || !(params[2] instanceof String)
     || !(params[3] instanceof Float)) {
    throw new RuntimeOperationsException(
      new IllegalArgumentException(
        "cannot invoke buildTShirt: "
          + "expecting params[i] instanceof SimpleData for i = 0 to 3"),
      "Wrong content for array Object[] params to invoke buildTShirt method");
   }
   // invoke addTShirt
   try {
    return addTShirt((String) params[0], (String) params[1],
      (String) params[2], (Float) params[3]);
   } catch (Exception e) {
    throw new MBeanException(e, "invoking buildTShirt: "
      + e.getClass().getName() + "caught [" + e.getMessage()
      + "]");
   }
  }

  else {
   throw new ReflectionException(new NoSuchMethodException(
     operationName), "Cannot find the operation "
     + operationName);
  }
  
 } 

 /**
     *
     */
 public MBeanInfo getMBeanInfo() {
  return OMBInfo;
 }

 /*  Open MBean Info */

 /**
     *
     */
 private void buildMBeanInfo() throws OpenDataException {

  OpenMBeanAttributeInfoSupport[] attributes = new OpenMBeanAttributeInfoSupport[2];
  OpenMBeanConstructorInfoSupport[] constructors = new OpenMBeanConstructorInfoSupport[1];
  OpenMBeanOperationInfoSupport[] operations = new OpenMBeanOperationInfoSupport[2];
  MBeanNotificationInfo[] notifications = new MBeanNotificationInfo[0];

  // attribute TShirts (no default or legal values: not supported for
  // tabular types anyway)
  attributes[0] = new OpenMBeanAttributeInfoSupport("TShirts",
    "List of available T-Shirts", tShirtsType, true, true, false);

  // attribute NbChanges (no default or legal values)
  attributes[1] = new OpenMBeanAttributeInfoSupport("NbChanges",
    "Number of times the TShirts list has been updated.",
    SimpleType.INTEGER, true, true, false);

  // constructor
  constructors[0] = new OpenMBeanConstructorInfoSupport(
    "OpenBean",
    "Constructs a OpenBean instance containing an empty TShirts list.",
    new OpenMBeanParameterInfoSupport[0]);

  // operation removeTShirt
  OpenMBeanParameterInfo[] params_remove = new OpenMBeanParameterInfoSupport[3];
  params_remove[0] = modelParamInfo;
  params_remove[1] = colorParamInfo;
  params_remove[2] = sizeParamInfo;

  operations[0] = new OpenMBeanOperationInfoSupport(
    "removeTShirt",
    "Removes the tShirt given in parameter to the list of available tShirts, "
      + "if a tshirt with the same index existed in the list, or does nothing otherwise.",
    params_remove, SimpleType.VOID, MBeanOperationInfo.ACTION);

  // operation buildTShirt
  OpenMBeanParameterInfo[] params_build = new OpenMBeanParameterInfoSupport[4];
  params_build[0] = modelParamInfo;
  params_build[1] = colorParamInfo;
  params_build[2] = sizeParamInfo;
  params_build[3] = priceParamInfo;
  operations[1] = new OpenMBeanOperationInfoSupport(
    "addTShirt",
    "Builds and returns a CompositeData TShirt instance from the specified parameters. "
      + "If parameter values are not legal according to the OpenMBeanParameterInfo instances for this method, "
      + "it throws an OpenDataException. "
      + "If model, color or size are null, it uses the default value provided in the OpenMBeanParameterInfo instances for this method.",
    params_build, SimpleType.VOID, MBeanOperationInfo.ACTION);

  // The OpenMBeanInfo
  OMBInfo = new OpenMBeanInfoSupport(this.getClass().getName(),
    "Open MBean", attributes, constructors, operations,
    notifications);
 }
}

2. Next perform the #4 and #5 steps same as you did in the previous example and you will see that you can expose an Heterogeneous objects by providing their mapping with the various Java Types.


Components Overview - Instrumentation - Notification Model

Notifications as the term itself describes that it is being used to emit the notifications for any change done to any of the resources and at other end there are several other resources who can register there intrerest in the listening of these notifications.
Important Components in Notification Model are: -

Listener - A listener which is shows interest and is registered to listen a certain type of Notifications.

Broadcaster/ Emitter - used to broadcast the notifications emitted by the resources.

Filter - Used for filtering the notifications and invoking listeners only for the relevant notifications.


As shown above the following Steps are followed in case of Notifications: -
1. Mbeans registers themselves with the MBean server as an Notification Broadcaster and emits Notifications which are consumed by the MBean Server.
2. Notification Listeners registers their interest for the notifications with the MBean Server
3. Mbean Server invokes listeners (which have registered their interest in receiving the notification) as soon any Notification is received from broadcasters.
4. Filters are applied before any notifications are processed by the Listeners.

Example: -

1. Define Bean and its management interface which exposes Attributes which needs to be managed and also broadcast the events for every change done with the Attributes.

package jmxSamples.mbeans.notifications.bean;

public interface MyBeanMBean {
 
 public String getName();

 public void setName(String name);

}


package jmxSamples.mbeans.notifications.bean;

import javax.management.AttributeChangeNotification;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;

public class MyBean extends NotificationBroadcasterSupport implements MyBeanMBean{

 private String name;
 
 private static volatile Long seqNum=0L; 

 /**
  * @return the name
  */
 public String getName() {
  return name;
 }

 /**
  * @param name the name to set
  */
 public void setName(String name) {
  
  Notification notification = 
            new AttributeChangeNotification(this,++seqNum,System.currentTimeMillis(),"Attribute of myBean Changed","name","java.lang.String", this.name, name);
   sendNotification(notification);
  this.name = name;
 
 }
 
}


2. Next Define a Listener which will listen to the notifications produced by the above Bean and also a Filter, which filters any unwanted notifications

package jmxSamples.mbeans.notifications;

import javax.management.Notification;
import javax.management.NotificationFilter;

public class MyNotificationFilter implements NotificationFilter{

 /**
  * 
  */
 private static final long serialVersionUID = -4542067417446721223L;

 public boolean isNotificationEnabled(Notification notification) {
  
  return true;
 }

}

package jmxSamples.mbeans.notifications;


import javax.management.AttributeChangeNotification;
import javax.management.Notification;
import javax.management.NotificationListener;

public class MyNotificationListener implements NotificationListener{

 public void handleNotification(Notification notification, Object handback) {
  
  if(notification instanceof AttributeChangeNotification){
   AttributeChangeNotification notificationA = (AttributeChangeNotification)notification;
  System.out.println("Notification receied");
  System.out.println(" Message = "+notificationA.getMessage());
  System.out.println(" SEQ Number = "+notificationA.getSequenceNumber());
  System.out.println(" Old Value = "+notificationA.getOldValue());
  System.out.println(" new Value = "+notificationA.getNewValue());
  System.out.println(" Type = "+notificationA.getType());
  
  }
  else{
   System.out.println("this is not a Attribute change Notification");
  }
 }
 
 

}


3. Lastly define a main class which registers the MBean and also associate the Listeners with this Bean.

package jmxSamples.mbeans.notifications;

import java.lang.management.ManagementFactory;

import javax.management.MBeanServer;
import javax.management.ObjectName;

import jmxSamples.mbeans.notifications.bean.MyBean;

public class RunNotifExample {

 /**
  * @param args
  */
 public static void main(String[] args) {
  new RunNotifExample().registerBean();
 }

 public void registerBean() {
  try {
   MyBean mBean = new MyBean();
   System.out.println("Starting Notificatio examplesss");
   ObjectName beanName = new ObjectName("mBean:type="+ mBean.getClass().getCanonicalName());
   MBeanServer server = ManagementFactory.getPlatformMBeanServer();
   
   server.registerMBean(mBean, beanName);
   server.addNotificationListener(beanName, new MyNotificationListener(), new MyNotificationFilter(), null);
   

   //Wait forever :)
   while(true){
    Thread.sleep(99999999);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}


4. Next perform the #4 and #5 steps same as you did in the previous example and you will see that you are able to receive all the notifications broadcasted by the registered Bean.

Some management Apps provide the flexibility to attach Listeners at runtime also


Components Overview - Agent level - Agent Architecture

Below digram clearly states the various resources managed by the Agent and how different ineterfaces to connect to Agents and get the resources.


MBean Server - One of the most important and CORE component of Agent, which not only represents managed resources but also Facilitates searching of Mbeans by its object name or a given pattern. All notification listners registeres their interest for any notification with MBean Server. MBean server can also be proxied by implementing "MBeanServerInvocationHandler".

Components Overview - Agent level - Agent Services - MLets

MLets defines a way to dynamically instantiate the MBeans. Bascially you define your MBeans in a file and these various MBeans are isntantiated and laoded at the runtime and registered within the MBean Server.
Apart from Dynamic Class Laoding it also helps loading and registration of MBeans over the network.

Example: -

1. Define a configuration file which contains the details of the Beans, which needs to be loaded and exposed. For simplicity name it as "mlet.conf" and place it at the root of the Application.
<MLET

CODE=jmxSamples.mbeans.standardBean.StandardBean
ARCHIVE=abc.jar
NAME=example:name=mletStandard >
</MLET>


2. Next define a main class which loads this configuration and registers the MBeans define in the above configuration file with the MBean server.

package 
package jmxSamples.mbeans.mlets;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.util.Set;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.loading.MLet;

public class RunMlets {

 /**
  * @param args
  */
 public static void main(String[] args) {
  try{
   MLet mlet = new MLet();

   ObjectName beanName = new ObjectName("mBean:type="+ mlet.getClass().getCanonicalName());
   MBeanServer server = ManagementFactory.getPlatformMBeanServer();
   System.out.println("Starting MLets ");
   //Register the MLet Bean
   server.registerMBean(mlet, beanName);
   
   //Define a configuration file which list down the Beans which needs to be registered
   File conf = new File("mlet.conf");
   //Add the configuration file object to the MLets
   mlet.addURL(conf.getAbsoluteFile().toURL());
   System.out.println(conf.getAbsoluteFile().toURL().toString());
   Set set = mlet.getMBeansFromURL(conf.getAbsoluteFile().toURL().toString());
   System.out.println(" here is the data = "+set);

   
   //Wait forever :)
   while(true){
    Thread.sleep(99999999);
   }
  }catch(Exception e){
   e.printStackTrace();
  }

 }

}


3. Next perform the #4 and #5 steps same as you did in the previous example and you will see that the beans define in the MLet Configuration file is available in JConsole.

Take care that the jar specified in the conf file should be available in the classpath of the application.

Components Overview - Agent level - Agent Services - Monitoring

Monitoring is services which is used to monitor the values defined by a particualr set of variables.

There are 3 kind of Monitors: -

Counter – Monitors the threshold values
Gauge – Monitors a High & Low threshold values
String – Monitors the String Values

All the above beans are based on the Observable Pattern, where the objects which needs to be monitored is defined at runtime.

Example: -

1. Define the All 3 types of beans along with their Management Interface.

COUNTER BEAN
package jmxSamples.mbeans.monitoring.counterBean;

public interface CounterBasicMBean {
 
 public int getCount();
 
 public void setCount(int count);

}


package jmxSamples.mbeans.monitoring.counterBean;

public class CounterBasic implements CounterBasicMBean{

 private int count;
 
 public int getCount() {

  return count;
 }

 public void setCount(int count) {
  this.count=count;
  
 }
 
 

}

GAUGE BEAN

package jmxSamples.mbeans.monitoring.gaugeBean;

public interface GaugeBasicMBean {

 public int getGcount();

 public void setGcount(int gcount);

}

package jmxSamples.mbeans.monitoring.gaugeBean;

public class GaugeBasic implements GaugeBasicMBean{

 private int gCount;

 public int getGcount() {

  return gCount;
 }

 public void setGcount(int gCount) {
  this.gCount = gCount;

 }

}

STRING BEAN


package jmxSamples.mbeans.monitoring.stringBean;

public interface StringBasicMBean {

 public void setName(String name);

 public String getName();

}


package jmxSamples.mbeans.monitoring.stringBean;

public class StringBasic implements StringBasicMBean{

 
 private String name;
 
 public String getName() {
  return name;
  
 }

 public void setName(String name) {
  this.name=name;
 }
 
 

}



2. Define a Listener, which will listen the notifications emitted by these beans

package jmxSamples.mbeans.monitoring;

import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.monitor.MonitorNotification;

public class MonitoringListener implements NotificationListener {

 public void handleNotification(Notification notification, Object handback) {
  System.out.println("received Notification");
  System.out.println(" Source = "
    + ((MonitorNotification) notification).getSource());

  System.out.println(" Type = "
    + ((MonitorNotification) notification).getType());

  MonitorNotification notif = (MonitorNotification) notification;
  if (notif.getType().equalsIgnoreCase(
    MonitorNotification.THRESHOLD_VALUE_EXCEEDED)) {

   System.out.println("Threshold Value Excedded for Attr "
     + notif.getObservedAttribute());
   System.out.println("Message = " + notif.getMessage());

  }
  if (notif.getType().equalsIgnoreCase(
    MonitorNotification.THRESHOLD_HIGH_VALUE_EXCEEDED)) {
   System.out.println("Threshold High Value Excedded for Attr "
     + notif.getObservedAttribute());
   System.out.println("Message = " + notif.getMessage());

  }
  if (notif.getType().equalsIgnoreCase(
    MonitorNotification.THRESHOLD_LOW_VALUE_EXCEEDED)) {

   System.out.println("Threshold Low Value Excedded for Attr "
     + notif.getObservedAttribute());
   System.out.println("Message = " + notif.getMessage());

  }

  if (notif.getType().equalsIgnoreCase(
    MonitorNotification.THRESHOLD_ERROR)) {

   System.out.println("Threshold Error Occured");
  }

 }

}


3. Now define a Main Class, which registers all types of Beans and also associate the listener with each of them

package jmxSamples.mbeans.monitoring;

import java.lang.management.ManagementFactory;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.monitor.CounterMonitor;
import javax.management.monitor.GaugeMonitor;
import javax.management.monitor.StringMonitor;

import jmxSamples.mbeans.monitoring.counterBean.CounterBasic;
import jmxSamples.mbeans.monitoring.gaugeBean.GaugeBasic;
import jmxSamples.mbeans.monitoring.stringBean.StringBasic;

public class RunMonitoringAgent {

 /**
  * Main Method
  * @param args
  */
 public static void main(String[] args) {
  try {
   
   RunMonitoringAgent agent = new RunMonitoringAgent();
   
   MBeanServer server = ManagementFactory.getPlatformMBeanServer();
   MonitoringListener listener = new MonitoringListener();
   
   agent.runCounterMonitor(server, listener);
   agent.runGaugeMonitor(server, listener);
   agent.runStringMonitor(server, listener);   

  } catch (Exception e) {
   e.printStackTrace();

  }

 }
 
 /**
  * Executes and registers the counter Monitor
  * @param server
  * @param listener
  */
 private void runCounterMonitor(MBeanServer server, MonitoringListener listener){
  try{
   
   CounterMonitor counterMonitor = new CounterMonitor();
   ObjectName counterMonitorName = new ObjectName("monitorName:name="
     + "javax.management.monitor.CounterMonitor");
   // Register CounterBean
   server.registerMBean(counterMonitor, counterMonitorName);
   
   // Add Listener to the Counter Bean
   counterMonitor.addNotificationListener(listener, null, null);
   ObjectName standardObsObjName = new ObjectName(
     "CounterBasicBean:name=CounterBasic");
   CounterBasic basicBean = new CounterBasic();
   // Register Basic MBean
   server.registerMBean(basicBean, standardObsObjName);

   // Set CounterBean Attributes
   counterMonitor.addObservedObject(standardObsObjName);
   counterMonitor.setObservedAttribute("Count");
   counterMonitor.setNotify(true);
   counterMonitor.setInitThreshold(6);
   counterMonitor.setOffset(2);
   counterMonitor.setGranularityPeriod(500);
   counterMonitor.start();
   
  }catch(Exception e){
   e.printStackTrace();
  }
 }
 
 /**
  * Sets the executes the Gauge Monitor
  * @param server
  * @param listener
  */
 private void runGaugeMonitor(MBeanServer server, MonitoringListener listener){
  try{
   GaugeMonitor gMon = new GaugeMonitor();
   
   ObjectName gMonitorName = new ObjectName("monitorName:name="
     + "javax.management.monitor.GauugeMonitor");
   
   // Register GaugeBean
   server.registerMBean(gMon, gMonitorName);
   
   // Add Listener to the Gauge Bean
   gMon.addNotificationListener(listener, null, null);
   
   ObjectName standardObsObjName = new ObjectName(
     "GaugeBasicBean:name=GaugeBasic");
   
   GaugeBasic gBasicBean = new GaugeBasic();
   // Register Gauge Basic MBean
   server.registerMBean(gBasicBean, standardObsObjName);
      
   //Set Gauge Parameters
   gMon.addNotificationListener(listener, null, null);
   gMon.setNotifyHigh(true);
   gMon.setNotifyLow(true);
   gMon.setGranularityPeriod(500);
   gMon.setThresholds(10, 6);
   gMon.setObservedAttribute("Gcount");
   gMon.addObservedObject(standardObsObjName);
   gMon.start();
   
   
  }catch(Exception e){
   e.printStackTrace();
  }
 }
 
 /**
  * Sets the String Monitor
  * @param server
  * @param listener
  */
 private void runStringMonitor(MBeanServer server, MonitoringListener listener){
  try{
   StringMonitor sMon = new StringMonitor();
   
   ObjectName sMonitorName = new ObjectName("monitorName:name="
     + "javax.management.monitor.StringMonitor");
   
   // Register GaugeBean
   server.registerMBean(sMon, sMonitorName);
   
   // Add Listener to the Gauge Bean
   sMon.addNotificationListener(listener, null, null);
   
   ObjectName standardObsObjName = new ObjectName(
     "StringBasicBean:name=StringBasic");
   
   StringBasic sBasicBean = new StringBasic();
   // Register Gauge Basic MBean
   server.registerMBean(sBasicBean, standardObsObjName);
      
   //Set Gauge Parameters
   sMon.addNotificationListener(listener, null, null);
   sMon.setNotifyDiffer(true);
   sMon.setNotifyMatch(true);
   sMon.setGranularityPeriod(500);
   sMon.setStringToCompare("Sumit");
   sMon.setObservedAttribute("Name");
   sMon.addObservedObject(standardObsObjName);
   sMon.start();
   
   
  }catch(Exception e){
   e.printStackTrace();
  }
 }

}


4. Next perform the #4 and #5 steps same as you did in the previous example and you will see that all the 3 beans are registered with their appropriate monitors and we can easily monitor them through our management Apps.

Components Overview - Agent level - Agent Services - Timer

Timer services is used to trigger the notifications in 2 different ways: -

1. At Specified Date or Time
2. At Constant Intervals

Example: -

1. Define a Listener which will listen to the notifications emitted by the Time Bean.

package jmxSamples.mbeans.timer;

import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.timer.TimerNotification;

public class TimerListener implements NotificationListener{

 public void handleNotification(Notification notification, Object handback) {
  TimerNotification notif = (TimerNotification)notification;
  
  System.out.println("Timer Notification received........");
  
  System.out.println("Type of message = " + notif.getType());
  System.out.println("Message = "+ notif.getMessage());
  System.out.println("User Data = "+ notif.getUserData());
  
  System.out.println("Timer Notification END........");

  
  
 }

}


2. Now define a Timer bean, register it with MBean server, associate the above Listener with the Bean and fire some notifications.

package jmxSamples.mbeans.timer;

import java.lang.management.ManagementFactory;
import java.util.Date;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.timer.Timer;

public class RunTimerAgent {

 /**
  * @param args
  */
 public static void main(String[] args) {
  try {

   RunTimerAgent agent = new RunTimerAgent();

   MBeanServer server = ManagementFactory.getPlatformMBeanServer();
   TimerListener listener = new TimerListener();

   agent.runTimerAgent(server, listener);
   
  } catch (Exception e) {
   e.printStackTrace();
  }

 }

 private void runTimerAgent(MBeanServer server, TimerListener listener) {
  try {
   
   Timer tim = new Timer();
   
   ObjectName timerMonitorName = new ObjectName("monitorName:name="
     + "javax.management.timer.Timer");
   // Register TimerBean
   server.registerMBean(tim, timerMonitorName);
   
   tim.addNotificationListener(listener, null, null);

  //Will trigger the notification only Once   
  tim.addNotification("String", "Hey I am Timer", new String(), new Date());

  //Will trigger the notification 50 times at the interval of 1 sec 
              tim.addNotification("String", "Hey I am Timer", new String(), new Date(), 1000, 50);
   tim.start();
   

  } catch (Exception e) {
   e.printStackTrace();
  }
 }

}


3. Next perform the #4 and #5 steps same as you did in the previous example and you will see that the events are triggered at regular intervals and Listeners are invoked with the appropriate message.

Components Overview - Agent level - Agent Services - Relation

Relation Sevice is used to define the n-array association between different Mbeans through the named Roles It helps in maintaiting Consistency between the various MBeans.

For example: - In an Library, users shuould not be allowed to register the books which are not available in the Stock .

The only information which is not visible is the apperance of relationship between different MBean's.

Example: -

1. Continuing the same Example lets define the relationship between the Books and their owners and also make sure that we are not having owners for the books which are available in the Library.

Lets define the Owner and Book MBeans: -

package jmxSamples.mbeans.relation.beans;

public interface SimpleOwnerMBean {
 public void setOwnerName(String ownerName);

 public String getOwnerName();
}

package jmxSamples.mbeans.relation.beans;

public class SimpleOwner implements SimpleOwnerMBean {
 private String m_name = null;

 
 public SimpleOwner() {
  
 }
 public SimpleOwner(String name) {
  m_name = name;
 }

 public void setOwnerName(String name) {
  m_name = name;
 }

 public String getOwnerName() {
  return m_name;
 }
}


package jmxSamples.mbeans.relation.beans;

public interface SimpleBooksMBean
{
   public void setBook(String bookName);

   public String getBook();
}


package jmxSamples.mbeans.relation.beans;

public class SimpleBooks implements SimpleBooksMBean
{
    private String m_name = null;

    public SimpleBooks(){
     
    }
    public SimpleBooks(String bookName)
    {
       m_name = bookName;
    }

    public void setBook(String bookName)
    {
       m_name = bookName;
    }

    public String getBook()
    {
       return m_name;
    }
 }



2. Now lets define the relation between the Owners and Books

package jmxSamples.mbeans.relation;

import javax.management.relation.RelationTypeSupport;
import javax.management.relation.RoleInfo;

public class SimplePersonalLibrary extends RelationTypeSupport {


 /**
  * 
  */
 private static final long serialVersionUID = 7029204967709861471L;
 

 public SimplePersonalLibrary(String relationTypeName) {
  super(relationTypeName);

  try {
   RoleInfo ownerRoleInfo = new RoleInfo("owner",
   // the name of the MBean class of which all members must be an
   // instance.
     "jmxSamples.mbeans.relation.beans.SimpleOwner", true, // read
     true, // write
     1, // only one owner
     1, // for 1 book
     "Owner");
   addRoleInfo(ownerRoleInfo);

   RoleInfo booksRoleInfo = new RoleInfo("books",
     "jmxSamples.mbeans.relation.beans.SimpleBooks", true, true,
     1, // and no fewer than 1
     4, // can only own max 4 books
     "Books");
   addRoleInfo(booksRoleInfo);
  } catch (Exception ex) {
   throw new RuntimeException(ex.getMessage());
  }
 }
}

3. Now lets define a Library which provides the access of Books to its various owners and at any point of time if we break any relation than exception is being thrown by the MBean Server.

package jmxSamples.mbeans.relation;

import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Iterator;

import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.relation.Role;
import javax.management.relation.RoleList;
import javax.management.relation.RoleResult;

import jmxSamples.mbeans.relation.beans.SimpleBooks;
import jmxSamples.mbeans.relation.beans.SimpleOwner;

public class RunRelationAgent {

 public static void main(String[] args) {

  try {
   RunRelationAgent agent = new RunRelationAgent();
   MBeanServer server = ManagementFactory.getPlatformMBeanServer();

   agent.defineRelation(server);

  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 private void defineRelation(MBeanServer server) {
  try {

   String m_relationServiceClass = "javax.management.relation.RelationService";

   Object[] params = { new Boolean(true) };
   String[] signature = { "boolean" };
   ObjectName m_relationObjectName = new ObjectName("relations:class="
     + m_relationServiceClass);
   server.createMBean(m_relationServiceClass, m_relationObjectName,
     null, params, signature);

   String libraryTypeName = "personal_library";

   SimplePersonalLibrary m_library = new SimplePersonalLibrary(
     libraryTypeName);

   Object[] params1 = { m_library };
   String[] signature1 = { "javax.management.relation.RelationType" };
   server.invoke(m_relationObjectName, "addRelationType", params1,
     signature1);
   printRelationTypeInfo(server, m_relationObjectName);

   createBooksOwners(server, libraryTypeName, m_relationObjectName);

  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 private void createBooksOwners(MBeanServer server, String libraryTypeName,
   ObjectName m_relationObjectName) {

  try {

   String personalLibraryId = libraryTypeName + "_internal";
   
   
   String ownerClassName = "jmxSamples.mbeans.relation.beans.SimpleOwner";
   String bookClassName = "jmxSamples.mbeans.relation.beans.SimpleBooks";

   System.out.println("Creating MBeans to represent our relations");

   ObjectName ownerName1 = new ObjectName("library:name="
     + ownerClassName + "1");
   ObjectName ownerName2 = new ObjectName("library:name="
     + ownerClassName + "2");
   ObjectName bookName1 = new ObjectName("library:name="
     + bookClassName + "1");
   ObjectName bookName2 = new ObjectName("library:name="
     + bookClassName + "2");
   ObjectName bookName3 = new ObjectName("library:name="
     + bookClassName + "3");
   ObjectName bookName4 = new ObjectName("library:name="
     + bookClassName + "4");
   ObjectName bookName5 = new ObjectName("library:name="
     + bookClassName + "5");

   SimpleOwner owner1 = new SimpleOwner("Fred");
   server.registerMBean(owner1, ownerName1);
   
   SimpleOwner owner2 = new SimpleOwner("HP");
   server.registerMBean(owner2, ownerName2);
   
   SimpleBooks book1 = new SimpleBooks("Lord of the rings1");
   server.registerMBean(book1, bookName1);

   SimpleBooks book2 = new SimpleBooks("Lord of the rings2");
   server.registerMBean(book2, bookName2);

   SimpleBooks book3 = new SimpleBooks("Lord of the rings3");
   server.registerMBean(book3, bookName3);

   SimpleBooks book4 = new SimpleBooks("Lord of the rings4");
   server.registerMBean(book4, bookName4);

   SimpleBooks book5 = new SimpleBooks("Lord of the rings5");
   server.registerMBean(book5, bookName5);

   // Create Owner List and Role
   ArrayList ownerList = new ArrayList();
   ownerList.add(ownerName1); // can only add owner to an owner role

   Role ownerRole = new Role("owner", ownerList);

   // Create Book List and Role
   ArrayList bookList = new ArrayList();
   bookList.add(bookName1);
   bookList.add(bookName2);
   bookList.add(bookName3);
   Role bookRole = new Role("books", bookList);

   // Create Library RoleList
   RoleList libraryList = new RoleList();
   libraryList.add(ownerRole);
   libraryList.add(bookRole);

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

   printAllRelationInfo(server, m_relationObjectName);
   
   //Remove 2 books
   server.unregisterMBean(bookName1);
         server.unregisterMBean(bookName2);
         System.out.println("2 Books are removed succesfully");
         System.out.println("Now removing one more book");
         server.unregisterMBean(bookName3);
         testAllAccessQueries(personalLibraryId, server, m_relationObjectName);
         
   while(true){
    Thread.sleep(999999);
   }

  } catch (Exception e) {
   e.printStackTrace();
  }

 }

 private void printRelationTypeInfo(MBeanServer server,
   ObjectName m_relationObjectName) {
  try {
   ArrayList relTypeNameList = (ArrayList) (server.getAttribute(
     m_relationObjectName, "AllRelationTypeNames"));
   System.out
     .println("The RelationType Names found in the RelationService: "
       + relTypeNameList.toString());
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }

 private void printAllRelationInfo(MBeanServer server,
   ObjectName m_relationObjectName) {

  try {
   ArrayList allRelationIds = (ArrayList) server.getAttribute(
     m_relationObjectName, "AllRelationIds");
   for (Iterator i = allRelationIds.iterator(); i.hasNext();) {
    String currentRelationId = (String) i.next();
    System.out.println("All RelationIds: " + currentRelationId);
    testAllAccessQueries(currentRelationId, server,
      m_relationObjectName);
   }
  } catch (Exception ex) {

   ex.printStackTrace();
  }
 }

 private void testAllAccessQueries(String relationId, MBeanServer server,
   ObjectName m_relationObjectName) {

  // retrieve all roles
  try {
   Object[] params = { relationId };
   String[] signature = { "java.lang.String" };
   RoleResult roleResult = (RoleResult) (server.invoke(
     m_relationObjectName, "getAllRoles", params, signature));
   RoleList roleList = roleResult.getRoles();
   for (Iterator i = roleList.iterator(); i.hasNext();) {
    Role currentRole = (Role) i.next();
    System.out.println(">>>> role name: "
      + currentRole.getRoleName());
    System.out.println(">>>> role values: "
      + currentRole.getRoleValue().toString());
   }
   System.out.println("No unresolved Roles roleUnresolved size: "
     + roleResult.getRolesUnresolved().size());
  } catch (Exception ex) {
   ex.printStackTrace();
  }

 }

}



JMX Security

JMX Security Model is based on the java Security Model.
So it works in same manner as it works for the other principals.

It defines 3 Basic Permission Classes, which are used to provide the different level of permissions: -

1.MBeanServerPermission - create/ find/ release of MBean Server
2.MBeanTrustPermission - defines only 1 permission – “register” and controls what MBeans can registered by the various servers
3.MBeanPermission - Permissions related to Mbean operations

Lets see a running example which shows how exactly these permissions works: -

1. Define a Policy Files which enables only the "createMBean" permissions and save it to some location and give it the name as "myJMX.Policy"

permission javax.management.MBeanServerPermission "createMBeanServer";
 //permission javax.management.MBeanServerPermission "findMBeanServer";
 //permission javax.management.MBeanServerPermission "newMBeanServer";
 //permission javax.management.MBeanServerPermission "releaseMBeanServer";
 //permission javax.management.MBeanPermission "addNotificationListener", "*";
 
 //permission javax.management.MBeanPermission "getAttribute", "*";
 //permission javax.management.MBeanPermission "getClassLoader", "*";
 //permission javax.management.MBeanPermission "getClassLoaderFor", "*";
 //permission javax.management.MBeanPermission "getClassLoaderRepository", "*";
 //permission javax.management.MBeanPermission "getMBeanInfo", "*";
 //permission javax.management.MBeanPermission "getObjectInstance", "*";
 //permission javax.management.MBeanPermission "instantiate", "*";
 //permission javax.management.MBeanPermission "invoke", "*";
 //permission javax.management.MBeanPermission "isInstanceOf", "*";
 //permission javax.management.MBeanPermission "queryMBeans", "*";
 //permission javax.management.MBeanPermission "queryNames", "*";
 //permission javax.management.MBeanPermission "registerMBean", "*";
 //permission javax.management.MBeanPermission "removeNotificationListener,"
 //permission javax.management.MBeanPermission "setAttribute", "*";
 //permission javax.management.MBeanPermission "unregisterMBean", "*";
 
 //permission javax.management.MBeanTrustPermission "register";

2. Define a java Class which tries to violate or test these permissions

package jmxSamples.mbeans.security;

import java.util.ArrayList;

import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;

public class RunMBeanServerPermissionAgent {

 /**
  * @param args
  */
 public static void main(String[] args) {

  RunMBeanServerPermissionAgent secAgent = new RunMBeanServerPermissionAgent();
  secAgent.displayMbeanServerPermission();

 }

 private void displayMbeanServerPermission() {
  try {

   MBeanServer server = null;
   try {

    // creating severSever
    server = MBeanServerFactory.createMBeanServer();

    System.out
      .println("Congratulations...You have the permissions to \"create MBean Server\"");

    System.out.println("Printing Mbeans in new Server = "
      + server.getMBeanCount());

   } catch (SecurityException se) {
    System.out
      .println("Sorry you dont have permission to create new MBean Sever");

   }

   try {
    ArrayList mServers = MBeanServerFactory
      .findMBeanServer(null);
    System.out
      .println("Congratulations...You have the permissions to \"Find MBean Server\"");
    System.out.println("Got " + mServers.size() + " MBean Servers");

   } catch (SecurityException se) {
    System.out
      .println("Sorry you dont have permission to search for MBean Servers");
   }

  } catch (Exception e) {
   e.printStackTrace();
  }

 }

}


3. Next perform the #4 and #5 steps same as you did in the previous example and also provide the policy file as an added argument: -

-Djava.security.manager -Djava.security.policy=D:/jdk1.6.0_23/Java/jre6/lib/security/myJMX.policy

Now your code will start respecting the policies define in the policy file and will throw the exception for all those permissions which have not been defined in the policy file.

JMX Implementations

The whole JMX implementation is divided into 2 parts: -

JMX Implementations - It is more of the implementation of the JSR-3, where it talks about the implementation of the standard specifications provided by Sun.
Some of popular implementations for the same are: -

- JDK1.5+
- mx4J (http://mx4j.sourceforge.net/)
- Sun reference Implementation (Java DMK5.1)

JMX Management Apps - Which provides the UI and connector to play around with the Data/ Beans exposed by the remote JMX Agent.

Basically these management Apps implement JSR-160 to provide a standard way of connecting to remote JMX Agent.

Few of the popular JMX management Apps are -

- JConsole
- Mx4J (http://mx4j.sourceforge.net/)
- Jolokia (http://www.jolokia.org/)




Customizing JMX Output

This is a very important aspect of deciding upon any of the JMX management Apps.
Till date all management apps do supports the representation of widely accepted data types like String, Integer, Map, List etc and these covers mostly our 75% of the requirements.

But there might be scenarios where we still need to support the display and modifications of custom objects.

Though it mainly depends upon the flexibility provided by the management Apps but most of the popular apps like mx4J do provide the various ways to customize the output produced by the Agents.

For e.g.
mx4j uses XSLT processor to define the views for the various datatypes.
It defines the various XSL files which can be extended (new ones can also be created) for the any Custom data types.

Enhancements in JDK1.6

A new Type of bean is introduced in Mustang (Java6) i.e. MXBeans,

Though MXBeans were initially introduced with Java5 but it didn't provide any API's to define the user defined MXBeans, though some standard MXBeans like MemoryMXBean, ThreadMXBean, RuntimeMXBean, GarbageCollectorMXBean etc were readily available and exposed by JVM

Java6 introduced API's to define the user defined MXBeans.

The idea behind the MXBeans was to define a convenient way to bundle related values
together in an MBean without requiring clients to be configured to handle the bundles.

And to achieve this MXBean were introduced which are based on the concepts of OpenBeans and StandardBeans: -

1. They rely on Open/ Standard Datatypes same as what is defined for the OpenBeans
2. They are much like the StandardBeans but with few notable differences: -
- Your Class and Interface need not follow the naming Conventions, as it was defined by MBeans.
- Management Interfaces can be annotated with "@MXBean" annotation.

Example: -

1. Define a Management interface (same as we did for StandardBeans) and either annotate it with "@MXBean" or use "MXBean" at the end of the Interface name.

package jmxSamples.mbeans.mxBeans;

//@MXBean annotation can also be used 
public interface TestNameIntfMXBean {
 
 public String getName();
 
 public void setName(String name);

}



2. Define an implementation Class.

Note - Implementation class doesn't follow the convention of MBeans

package jmxSamples.mbeans.mxBeans;

public class TestNameClass implements TestNameIntfMXBean {

 String name ="initial value";
 public String getName() {
  
  return name;
 }

 public void setName(String name) {
  this.name=name;
  
 }
 
}


3. Now define a simple Main Class which registers the MBean with the MBean Server.

package jmxSamples.mbeans.mxBeans;

import java.lang.management.ManagementFactory;

import javax.management.MBeanServer;
import javax.management.ObjectName;

public class RunMXBeans {

 /**
  * @param args
  */
 public static void main(String[] args) {
  try {
   TestNameIntfMXBean mBean = new TestNameClass();

   MBeanServer server = ManagementFactory.getPlatformMBeanServer();
   server.registerMBean(mBean, new ObjectName("mBean:type="
     + mBean.getClass().getCanonicalName()));
   
   
   

   while (true) {
    Thread.sleep(999999);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

}


4. Next perform the #4 and #5 steps same as you did in the previous example and you will see the same behavior as Standard Beans.

Follow this Link to get more understanding and info on MXBeans


Apart from the above there are few Standard MXBeans which are already exposed by JVM and are available to the programmers through the "ManagementFactory" class: -

1. CompilationMXBean - Exposes the attributes and operations related Compilation System managed by the JVM.
2. GarbageCollectorMXBean - Exposes the attributes and operations related garbage collection System managed by JVM
3. MemoryMXBean - Exposes the attributes and operations related Memory like heap, non-heap, gc etc
4. MemoryManagerMXBean- Exposes the attributes and operations related Memory Managers like "CodeCacheManager"
5. ThreadMXBean - Exposes attributes like AllThreads, ThreadCount, PeakThreadCount and many more.
6. OperatingSystemMXBean - Exposes the attributes of OS like physical/virtual Memory, swap space, free memory etc.
7. RuntimeMXBean - Exposes attributes like details about JVM Specs (version, vendor, name etc), classpath, bootclasspath etc
8. ClassLoadingMXBean - Details about the total loaded classes unloaded classes, verbose mode etc.
9. MemoryPoolMXBean - Exposes the attributes and operations related various memory pools like eden, perm gen, Survivor Space, Tenured Gen etc

ManagementFactory.java defines various Static methods to get the references of all the above all the above MXBeans.

For more details Please visit this Link

General Tips & Tricks

Connecting to the JMX Agent Programmatically

JMXServiceURL u = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi:// “ + hostName + ":" + portNum + "/jmxrmi");
JMXConnector c = JMXConnectorFactory.connect(u);

Remotely monitoring and Connecting the JMX Agents

You can use RMI also to remotely connect to the running JMX Agents.
Provide the below URL in JConsole and select the "Remote Process" Option.

"service:jmx:rmi:///jndi/rmi://:/jmxrmi"

MX4j and other apps also provide the same kind of flexibility

Behind the Firewall

Usually the direct access to IP-Address is not allowed and the Firewall security restrictions forces to use the host name for remote connections.

So while defining the java process, apart from other JMX parameters also define the below param

-Djava.rmi.server.hostname=<public-name of your System>

And than client Apps can connect to the JMX Agents by using the name specified by the above param.

Exposing Agents over HTTP

There are many management apps like mx4j, Java-DMK which defines the HttpAdaptor for exposing all Beans exposed by the Agent over HTTP.
They also provide a decent UI interface for exposing the beans. see here

Securing Your JMX Agents

Certainly in production kind of environments you will definitely need some mechanism to secure your Agents from un-authorized access.

There are 3 different ways by which you can secure your JMX Agents and provide an secure way of access to your Clients: -

1. Using SSL - It implements and uses the concepts of JSSE. Visit this Link for more info.
2. Using Password and Access Files

Also refer to the below Snapshot which defines all the runtime properties which are exposed by JMX (Taken From Here): -


No comments: