Given a resource in the Java programming language, either an application, a service or an object representing a device, its instrumentation is the way that you expose its management interface. The management interface is the set of attributes and operations that are visible to managers wishing to interact with that resource. Therefore, instrumenting a resource makes it manageable.
This lesson covers the three ways to instrument a resource: by writing a standard MBean, by implementing a dynamic MBean, or by dynamically instantiating a configurable model MBean.
When you write a standard MBean, you follow certain design patterns so that the method names in your object exposes the attributes and operations to static introspection. Dynamic MBeans all implement a generic interface and may expose a rich description of their management interface. Model MBeans are MBean templates whose management interface and resource target are defined at runtime.
This lesson contains the following topics:
"Standard MBeans" shows how to write a standard MBean by following the design patterns defined by the Java Management extensions. The example shows how an agent then accesses the attributes and operations.
"Dynamic MBeans" shows how to implement the DynamicMBean interface in order to expose a coherent management interface. Running the example highlights the similarities and differences between dynamic and standard MBeans, with an analysis of performance issues.
"Model MBeans" gives an example of how to create a model MBean, configure its behavior, set its target object, and then manage it in the same way as any other MBean.
A standard MBean is the simplest and fastest way to instrument a resource from scratch: attributes and operations are simply methods which follow certain design patterns. A standard MBean is composed of the MBean interface which lists the methods for all exposed attributes and operations, and of the class which implements this interface and provides the functionality of the resource.
The code samples in this topic are taken from the files in the StandardMBean example directory located in the main examplesDir (see "Directories and Classpath" in the preface).
Contents:
"Exposing the MBean Interface" demonstrates the design patterns for attributes and operations and gives some general rules for writing the MBean interface.
"Implementing the MBean" shows how the MBean interface is related to the code for the manageable resource.
"Running the Standard MBean Example" demonstrates the runtime behavior of a standard MBean.
Typically, you would first determine the management interface of your resource, that is the information needed to manage it. This information is expressed as attributes and operations. An attribute is a value of any type that a manager can get or set remotely. An operation is a method with any signature and any return type that the manager can invoke remotely.
Attributes and operations are conceptually equivalent to properties and actions on JavaBeans objects. However, their translation into Java code is entirely different to accommodate the management functionality.
As specified by the Java Management extensions for instrumentation, all attributes and operations are explicitly listed in an MBean interface. This is a Java interface that defines the full management interface of an MBean. This interface must have the same name as the class that implements it, followed by the MBean suffix. Since the interface and its implementation are usually in different files, there are two files which make up a standard MBean.
For example, the class SimpleStandard (in the file SimpleStandard.java) will have its management interface defined in the interface SimpleStandardMBean (in the file SimpleStandardMBean.java).
public interface SimpleStandardMBean { public String getState() ; public void setState(String s) ; public Integer getNbChanges() ; public void reset() ; } |
Only public methods in the MBean interface are taken into consideration for the management interface. When present, non-public methods should be grouped separately, to make the code clearer for human readers.
Attributes are conceptual variables that are exposed for management through getter and setter methods in the MBean interface:
A getter is any public method whose name begins with get and which doesn't return void; it lets a manager read the value of the attribute, whose type is that of the returned object
A public method whose name begins with is and which returns a boolean or Boolean object is also a getter, though a boolean attribute may have only one getter (it must be one form or the other)
A setter is any public method whose name begins with set and which takes a single parameter; it lets a manager write a new value in the attribute, whose type is that of the parameter
Attribute types can be arrays of objects, but individual array elements cannot be accessed individually through the getters and setters. Use operations to access the array elements, as described below. The following code example demonstrates an attribute with an array type:
public String[] getMessages(); public void setMessages(String[] msgArray);
The name of the attribute is the literal part of the method name following get, is, or set. This name is case sensitive in all Java Dynamic Management Kit objects that manipulate attribute names. Using these patterns, we can determine the attributes exposed in the code sample above:
State is a readable and writeable attribute of type String
NbChanges is a read-only attribute of type Integer
The specification of the design patterns for attributes implies the following rules:
Attributes may be read-only, write-only, or readable and writeable
Attribute names cannot be overloaded: for any given attribute name there can be at most one setter and one getter, and if both are defined, they must use the same type
Operations are methods that management applications can call remotely on a resource. They can be defined with any number of parameters of any type and can return any type.
The design patterns for operations are simple: any public method defined in the MBean interface that is not an attribute getter or setter is an operation. For this reason, getters and setters are usually declared first in the Java code, so that all operations are grouped afterwards. The name of an operation is the name of the corresponding method.
The SimpleStandardMBean in the example defines one operation, reset, which takes no parameters and returns nothing.
While the following methods define valid operations (and not attributes), these types of names should not be used to avoid confusion:
public void getFoo(); public Integer getBar(Float p); public void setFoo(Integer one, Integer two); public String isReady();
For performance reasons, you may want to define operations for accessing individual elements of an array type attribute. In this case, use non-ambiguous operation names:
public String singleGetMessage(int index); public void singleSetMessage(int index, String msg);
The Java Dynamic Management Kit imposes no restrictions on attribute types, operation attribute types, and operation return types. However, the developer must insure that the corresponding classes are available to all applications manipulating these objects, and that they are compatible with the type of communication used. For example, attribute and operation types must be serializable in order to be manipulated remotely using the RMI or HTTP protocol.
The second part of an MBean is the class that implements the MBean interface. This class encodes the expected behavior of the manageable resource in its implementation of the attribute and operation methods. Of course, the resource does not need to reside entirely in this class, the MBean implementation can rely on other objects.
Beyond the implementation of the corresponding MBean interface, there are two requirements on the MBean class:
It must be a concrete class so that it can be instantiated
It must expose at least one public constructor so that any other class can create an instance
Otherwise, the developer is free to implement the management interface in any way, provided of course that the object has the expected behavior. Here is the sample code that implements our MBean interface:
public class SimpleStandard implements SimpleStandardMBean { public String getState() { return state; } public void setState(String s) { state = s; nbChanges++; } public Integer getNbChanges() { return new Integer(nbChanges); } public void reset() { state = "initial state"; nbChanges = 0; nbResets++; } // This method is not a getter in the management sense because // it is not exposed in the "SimpleStandardMBean" interface. public Integer getNbResets() { return new Integer(nbResets); } // internal variables for exposed attributes private String state = "initial state"; private int nbChanges = 0; // other private variables private int nbResets = 0; } |
In this example there is no constructor. Since the Java compiler provides a public, no-argument constructor by default in such cases, this is a valid MBean.
As in this example, attributes are usually implemented as internal variables whose value is returned or modified by the getter and setter methods. However, an MBean may implement any access and storage scheme to fit particular management needs, provided getters and setters retain their read and write semantics. Methods in the MBean implementation may have side-effects, but it is up to the programmer to insure that these are safe and coherent within the full management solution.
As we shall see later, management applications never have a direct handle on an MBean. They only have an identification of an instance and the knowledge of the management interface. In this case, the mechanism for exposing attributes through methods in the MBean interface makes it impossible for an application to access the MBean directly. Internal variables and methods, and even public ones, are totally encapsulated and their access is controlled by the programmer through the implementation of the MBean interface.
The examplesDir/StandardMBean directory contains the SimpleStandard.java and SimpleStandardMBean.java files which make up the MBean. This directory also contains a simple agent application which instantiates this MBean, introspects its management interface and manipulates its attributes and operations.
Compile all files in this directory with the javac command. For example, on the Solaris platform with the Korn shell, you would type:
$ cd examplesDir/StandardMBean/ $ javac -classpath classpath *.java |
To run the example, launch the agent class which will interact with the SimpleStandard MBean:
$ java -classpath classpath StandardAgent |
Press <Enter> when the application pauses to step through the example. The agent application handles all input and output in this example and gives us a view of the MBean at runtime.
We will look at how agents work in "Dynamic Agents", but this example demonstrates how the MBean interface limits the view of what the MBean exposes for management. Roughly, the agent introspects the MBean interface at runtime to determine what attributes and operations are available. You then see the result of calling the getters, setters and operations.
The lesson on agents will also cover the topics of object names and exceptions which you see when running this example.
A dynamic MBean implements its management interface programmatically, instead of through static method names. To do this, it relies on metadata classes which represent the attributes and operations exposed for management. Management applications then call generic getters and setters whose implementation must resolve the attribute or operation name to its intended behavior.
One advantage of this instrumentation is that you can use it to quickly make an existing resource manageable. The implementation of the DynamicMBean interface can provide an instrumentation wrapper for an existing resource.
Another advantage is that the metadata classes for the management interface can provide human-readable descriptions of the attributes, operations and MBean itself. This information could be displayed to a user on a management console to describe how to interact with this particular resource.
The code samples in this topic are taken from the files in the DynamicMBean example directory located in the main examplesDir (see "Directories and Classpath" in the preface).
Contents:
"Exposing the Management Interface" explains the DynamicMBean interface and its generic methods common to all dynamic MBeans.
"Implementing a Dynamic MBean" shows how to implement this interface to expose specific attributes and operations.
"Running the Dynamic MBean Example" demonstrates the runtime behavior of a dynamic MBean.
In the standard MBean, attributes and operations are exposed statically in the names of methods in the MBean interface. Dynamic MBeans all share the same interface which defines generic methods to access attributes and operations. Since the management interface is no longer visible through introspection, dynamic MBeans must also provide a description of their attributes and operations explicitly.
The DynamicMBean class is a Java interface defined by the Java Management extensions. It specifies the methods that a resource implemented as a dynamic MBean must provide to expose its management interface. Here is an uncommented version of the code:
public interface DynamicMBean { public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException; public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException ; public AttributeList getAttributes(String[] attributes); public AttributeList setAttributes(AttributeList attributes); public Object invoke( String actionName, Object params[], String signature[]) throws MBeanException, ReflectionException ; public MBeanInfo getMBeanInfo(); } |
The getMBeanInfo method is the one which provides a description of the MBean's management interface. This method returns an MBeanInfo object which contains the metadata information about attributes and operations.
The attribute getters and setters are generic, since they take the name of the attribute which needs to be read or written. For convenience, dynamic MBeans must also define bulk getters and setters to operate on any number of attributes at once. These methods use the Attribute and AttributeList classes to represent attribute name-value pairs and lists of name-value pairs, respectively.
Since the names of the attributes are not revealed until runtime, the getters and setters are necessarily generic. In the same way, the invoke method takes the name of an operation and its signature, in order to invoke any method which might be exposed.
As a consequence of implementing generic getters, setters, and invokers, the code for a dynamic MBean is more complex than for a standard MBean. For example, instead of having a specific getter called by name, the generic getter must verify the attribute name and then encode the functionality to read each of the possible attributes.
A dynamic MBean has the burden of building the description of its own management interface. The JMX specification defines the Java objects used to completely describe the management interface of an MBean. Dynamic MBeans use these objects to provide a complete self description as returned by the getMBeanInfo method. Agents also use these classes to describe a standard MBean after it has been introspected.
As a group, they are referred to as the MBean metadata classes because they provide information about the MBean. This information includes the attributes and operations of the management interface but also the list of constructors for the MBean class and the notifications that the MBean may send. Notifications are event messages that are defined by the JMX architecture; they are fully covered in "The Notification Mechanism".
Each element is described by its metadata object containing its name, a description string, and its characteristics. For example, an attribute has a type and is readable and/or writeable. The following table lists all MBean metadata classes:
Class Name |
Purpose |
---|---|
MBeanInfo | Top-level object containing arrays of metadata objects for all MBean elements; also includes the name of the MBean's Java class and a description string |
MBeanFeatureInfo | Parent class from which all other metadata objects inherit a name and a description string |
MBeanOperationInfo | Describes an operation: the return type, the signature as an array of parameters, and the impact (whether the operation just returns information or modifies the resource) |
MBeanConstructorInfo | Describes a constructor by its signature |
MBeanParameterInfo | Gives the type of a parameter in an operation or constructor signature |
MBeanAttributeInfo | Describes an attribute: its type, whether it is readable, and whether it is writeable |
MBeanNotificationInfo | Contains an array of notification type strings |
A dynamic MBean consists of a class that implements the DynamicMBean interface coherently. By this, we mean a class which exposes a management interface whose description matches the attributes and operations which are accessible through the generic getters, setters and invokers.
MBeans are not allowed to be both standard and dynamic. When a class is instantiated as an MBean, the agent checks the interfaces that it implements. If the class implements or inherits an implementation of both the corresponding MBean interface and the DynamicMBean interface, then an exception is raised and the MBean cannot be created.
Beyond this restriction, a dynamic MBean must also follow the same two rules as a standard MBean, namely:
It must be a concrete class so that it can be instantiated
It must expose at least one public constructor so that any other class can create an instance
Thereafter, a dynamic MBean class is free to declare any number of public or private methods and variables. None of these are visible to management applications, only the methods implementing the DynamicMBean interface are exposed for management. A dynamic MBean is also free to rely on other classes which may be a part of the manageable resource.
An MBean is a manageable resource that exposes a specific management interface. The name dynamic MBean refers to the fact that the interface is revealed at runtime, as opposed to through the introspection of static class names. The term dynamic is not meant to imply that the MBean can dynamically change its management interface. The management architecture defined by JMX and implemented in the Java Dynamic Management Kit does not support MBeans whose management interface is modified during runtime.
This is not an issue with standard MBeans which would need to be recompiled in order to change their interface. However, dynamic MBeans could be programmed so that their interface description and their generic getters, setters and the invoker have a different behavior at different times. In practice, this type of MBean could be created but it couldn't be managed after any change of interface.
As a rule, the value returned by the MBeanInfo method of a dynamic MBean, and the corresponding behavior of getters, setters and the invoker, must never change over the lifetime of a given instance of the MBean. However, it is permissible to have the same dynamic MBean class expose different management interfaces depending upon the instantiation conditions. This would be a valid MBean, since the agent architecture manages object instances, not class types. It would also be a very advanced MBean for a complex management solution, beyond the scope of this tutorial.
Since the MBean description should never change, it is usually created once at instantiation, and the getMBeanInfo method just returns its reference at every call. The MBean constructor should therefore build the MBeanInfo object from the MBean metadata classes such that it accurately describes the management interface. And since most dynamic MBeans will always be instantiated with the same management interface, building the MBeanInfo object is fairly straightforward.
The following code shows how the SimpleDynamic MBean defines its management interface, as built at instantiation and returned by its getMBeanInfo method:
// class constructor public SimpleDynamic() { buildDynamicMBeanInfo(); } // internal variables describing the MBean private String dClassName = this.getClass().getName(); private String dDescription = "Simple implementation of a dynamic MBean."; // internal variables for describing MBean elements private MBeanAttributeInfo[] dAttributes = new MBeanAttributeInfo[2]; private MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1]; private MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1]; private MBeanInfo dMBeanInfo = null; // internal method private void buildDynamicMBeanInfo() { dAttributes[0] = new MBeanAttributeInfo( "State", // name "java.lang.String", // type "State: state string.", // description true, // readable true); // writable dAttributes[1] = new MBeanAttributeInfo( "NbChanges", "java.lang.Integer", "NbChanges: number of times the State string has been changed.", true, false); // use reflection to get constructor signatures Constructor[] constructors = this.getClass().getConstructors(); dConstructors[0] = new MBeanConstructorInfo( "SimpleDynamic(): No-parameter constructor", //description constructors[0]); // the contructor object MBeanParameterInfo[] params = null; dOperations[0] = new MBeanOperationInfo( "reset", // name "Resets State and NbChanges attributes to their initial values", // description params, // parameter types "void", // return type MBeanOperationInfo.ACTION); // impact dMBeanInfo = new MBeanInfo(dClassName, dDescription, dAttributes, dConstructors, dOperations, new MBeanNotificationInfo[0]); } // exposed method implementing the DynamicMBean.getMBeanInfo interface public MBeanInfo getMBeanInfo() { // return the information we want to expose for management: // the dMBeanInfo private field has been built at instantiation time, return(dMBeanInfo); } |
Generic getters and setters take a parameter that indicates the name of the attribute to read or write. There are two issues to keep in mind when implementing these methods:
Attribute names must be correctly mapped to their corresponding internal representation
Invalid attribute names and types should raise an exception, including when writing to a read-only attribute (and vice-versa)
The getAttribute method is the simplest, since only the attribute name must be verified:
public Object getAttribute(String attribute_name) throws AttributeNotFoundException, MBeanException, ReflectionException { // Check attribute_name to avoid NullPointerException later on if (attribute_name == null) { throw new RuntimeOperationsException( new IllegalArgumentException("Attribute name cannot be null"), "Cannot invoke a getter of " + dClassName + " with null attribute name"); } // Call the corresponding getter for a recognized attribute_name if (attribute_name.equals("State")) { return getState(); } if (attribute_name.equals("NbChanges")) { return getNbChanges(); } // If attribute_name has not been recognized throw(new AttributeNotFoundException( "Cannot find " + attribute_name + " attribute in " + dClassName)); } // internal methods for getting attributes public String getState() { return state; } public Integer getNbChanges() { return new Integer(nbChanges); } // internal variables representing attributes private String state = "initial state"; private int nbChanges = 0; |
The setAttribute method is more complicated, since you must also insure that the given type can be assigned to the attribute and handle the special case for a null value:
public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { // Check attribute to avoid NullPointerException later on if (attribute == null) { throw new RuntimeOperationsException( new IllegalArgumentException("Attribute cannot be null"), "Cannot invoke a setter of " + dClassName + " with null attribute"); } // Note: Attribute class constructor ensures the name not null String name = attribute.getName(); Object value = attribute.getValue(); // Call the corresponding setter for a recognized attribute name if (name.equals("State")) { // if null value, try and see if the setter returns any exception if (value == null) { try { setState( null ); } catch (Exception e) { throw(new InvalidAttributeValueException( "Cannot set attribute "+ name +" to null")); } } // if non null value, make sure it is assignable to the attribute else { try { if ((Class.forName("java.lang.String")).isAssignableFrom( value.getClass())) { setState((String) value); } else { throw(new InvalidAttributeValueException( "Cannot set attribute "+ name + " to a " + value.getClass().getName() + " object, String expected")); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } // optional: recognize an attempt to set a read-only attribute else if (name.equals("NbChanges")) { throw(new AttributeNotFoundException( "Cannot set attribute "+ name + " because it is read-only")); } // unrecognized attribute name else { throw(new AttributeNotFoundException( "Attribute " + name + " not found in " + this.getClass().getName())); } } // internal method for setting attribute public void setState(String s) { state = s; nbChanges++; } |
Notice that the generic getter and setter methods usually hard-code information about the attributes. If a change in your management solution requires you to change your management interface, it will be harder to do with a dynamic MBean. In a standard MBean, each attribute and operation is a separate method, so unchanged attributes are unaffected. In a dynamic MBean, you must modify these generic methods that encode all attributes.
The DynamicMBean interface includes bulk getter and setter methods for reading or writing more than one attribute at once. These methods rely on the following classes:
Class Name |
Purpose |
---|---|
Attribute | A simple object which contains the name string and value object of any attribute. |
AttributeList | A dynamically extendable list of Attribute objects (extends java.util.ArrayList) |
The AttributeList class extends the java.util.ArrayList class which is specific to Java 2. For this class and others that rely on similar sets and collection, the Java Dynamic Management Kit provides the collections.jar file for complete compatibility using any JDK version 1.1.x. See Directories and Classpath in the preface for more information.
The bulk getter and setter methods usually rely on the generic getter and setter, respectively. This makes them independent of the management interface, which can simplify certain modifications. In this case, their implementation consists mostly of error checking on the list of attributes. However, all bulk getters and setters must implement the following behavior: an error on any one attribute does not interrupt or invalidate the bulk operation on the other attributes.
If an attribute cannot be read, then its name-value pair does not figure in the list of results. If an attribute cannot be written, it will not be copied to the returned list of successful set operations. As a result, if there are any errors, the lists returned by bulk operators will not have the same length as the array or list passed to them. In any case, the bulk operators do not guarantee that their returned lists have the same ordering of attributes as the input array or list.
The SimpleDynamic MBean shows one way of implementing the bulk getter and setter methods:
public AttributeList getAttributes(String[] attributeNames) { // Check attributeNames to avoid NullPointerException later on if (attributeNames == null) { throw new RuntimeOperationsException( new IllegalArgumentException( "attributeNames[] cannot be null"), "Cannot invoke a getter of " + dClassName); } AttributeList resultList = new AttributeList(); // if attributeNames is empty, return an empty result list if (attributeNames.length == 0) return resultList; // build the result attribute list 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) { // print debug info but continue processing list e.printStackTrace(); } } return(resultList); } public AttributeList setAttributes(AttributeList attributes) { // Check attributesto avoid NullPointerException later on if (attributes == null) { throw new RuntimeOperationsException( new IllegalArgumentException( "AttributeList attributes cannot be null"), "Cannot invoke a setter of " + dClassName); } AttributeList resultList = new AttributeList(); // if attributeNames is empty, nothing more to do if (attributes.isEmpty()) return resultList; // try to set each attribute and add to result list if successful 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) { // print debug info but keep processing list e.printStackTrace(); } } return(resultList); } |
Finally, a dynamic MBean must implement the invoke method so that operations in the management interface can be called. This method requires the same considerations as the generic getter and setter:
Operations and their parameters should be mapped to their internal representation and the result must be returned
Operation names and parameter types need to be verified
These verifications are usually hard-coded, again making modifications to the management interface more delicate than in a standard MBean
The implementation in the SimpleDynamic MBean is relatively simple due to the one operation with no parameters:
public Object invoke( String operationName, Object params[], String signature[]) throws MBeanException, ReflectionException { // Check operationName to avoid NullPointerException later on if (operationName == null) { throw new RuntimeOperationsException( new IllegalArgumentException( "Operation name cannot be null"), "Cannot invoke a null operation in " + dClassName); } // Call the corresponding operation for a recognized name if (operationName.equals("reset")){ // this code is specific to the internal "reset" method: reset(); // no parameters to check return null; // and no return value } else { // unrecognized operation name: throw new ReflectionException( new NoSuchMethodException(operationName), "Cannot find the operation " + operationName + " in " + dClassName); } } // internal variable private int nbResets = 0; // internal method for implementing the reset operation public void reset() { state = "initial state"; nbChanges = 0; nbResets++; } // Method not revealed in the MBean description and not accessible // through "invoke" therefore it is only available for internal mgmt public Integer getNbResets() { return new Integer(nbResets); } |
As it is written, the SimpleDynamic MBean correctly provides a description of its management interface and implements its attributes and operations. However, this example demonstrates the need for a strict coherence between what is exposed by the getMBeanInfo method and what can be accessed through the generic getters, setters, and invoker.
A dynamic MBean whose getMBeanInfo method describes an attribute or operation which cannot be accessed is not compliant with the Java Management extensions and is technically not a manageable resource. Similarly, a class could make attributes or operations accessible without describing them in the returned MBeanInfo object. Since MBeans should raise an exception when an undefined attribute or operation is accessed, this would, again, technically not be a compliant resource.
The examplesDir/DynamicMBean directory contains the SimpleDynamic.java file which makes up the MBean. The DynamicMBean interface is defined in the javax.management package provided in the runtime jar file (jdmkrt.jar) of the Java Dynamic Management Kit. This directory also contains a simple agent application which instantiates this MBean, calls its getMBeanInfo method to get its management interface and manipulates its attributes and operations.
Compile all files in this directory with the javac command. For example, on the Solaris platform, you would type:
$ cd examplesDir/DynamicMBean/ $ javac -classpath classpath *.java |
To run the example, launch the agent class which will interact with the SimpleDynamic MBean:
$ java -classpath classpath DynamicAgent |
Press <Enter> when the application pauses to step through the example. The agent application handles all input and output in this example and gives us a view of the MBean at runtime.
This example demonstrates how the management interface encoded in the getMBeanInfo method is made visible in the agent application. We can then see the result of calling the generic getters and setters and the invoke method. Finally, the code for filtering attribute and operation errors is exercised, and we see the exceptions from the code samples as they are raised at runtime.
Now that we have implemented both types of MBeans we can compare how they are managed. We purposely created a dynamic MBean and a standard MBean with the same management interface so that we can do exactly the same operations on them. On the Solaris platform, we can compare the relevant code of the two agent applications with the diff utility (your output may vary):
$ cd examplesDir $ diff ./StandardMBean/StandardAgent.java ./DynamicMBean/DynamicAgent.java [...] 41c40 < public class StandardAgent { --- > public class DynamicAgent { 49c48 < public StandardAgent() { --- > public DynamicAgent() { 77c76 < StandardAgent agent = new StandardAgent(); --- > DynamicAgent agent = new DynamicAgent(); 88c87 < echo("\n>>> END of the SimpleStandard example:\n"); --- > echo("\n>>> END of the SimpleDynamic example:\n"); 113c112 < String mbeanName = "SimpleStandard"; --- > String mbeanName = "SimpleDynamic"; |
If the two agent classes had the same name, we see that the only programmatic difference would be the following:
113c112 < String mbeanName = "SimpleStandard"; --- > String mbeanName = "SimpleDynamic"; |
We can see that there is only one difference between the two example agents handling different types of MBeans: the name of the MBean class that is instantiated! In other words, standard and dynamic MBeans are indistinguishable from the agent's point of view. This is the power of the JMX architecture: managers interact with the attributes and operations of a manageable resource, and the specification of the agent hides any implementation differences between MBeans.
Since we know that the two MBeans are being managed identically, we can also compare their runtime behavior. In doing so, we can draw two conclusions:
The dynamic MBean was programmed to have the same behavior as the standard MBean; the example output shows that this is indeed the case: despite the different implementations, the functionality of the resource is strictly the same
The only functional difference between the two is that the agent can obtain the self-description strings encoded in the dynamic MBean: attributes and operations are associated with the explanation that the programmer provides for them
There is no mechanism which allows a standard MBean to provide a self-description. The MBean server provides a default description string for each feature in a standard MBean, and these descriptions are identical for all standard MBeans.
In the introduction to this topic we presented two structural advantages of dynamic MBeans, namely the ability to wrap existing code to make it manageable and the ability to provide a self-description of the MBean and its features. Another advantage is that using dynamic MBeans can lead to faster overall execution time.
The performance gain depends on the nature of the MBean and how it is managed in the agent. For example, the SimpleDynamic MBean, as it is used, is probably not measurably faster than the SimpleStandard example in the Chapter 1, Standard MBeans topic. When seeking improved performance, there are two situations which must be considered: MBean introspection, and management operations.
Since the dynamic MBean provides its own description, the agent doesn't need to introspect it as it would a standard MBean. Since introspection is done only once by the agent, this is a one-time performance gain during the lifetime of the MBean. In an environment where there are many MBean creations and where MBeans have a short lifetime, a slight performance increase can be measured.
However, the largest performance gain is in the management operations: calling the getters, setters and invoker. As we shall see in the next lesson ("Dynamic Agents"), the agent makes MBeans manageable through generic getters, setters, and invokers. In the case of standard MBeans, the agent must do the computations for resolving attribute and operation names according to the design patterns. Since dynamic MBeans necessarily expose the same generic methods, these are called directly by the agent. When a dynamic MBean has a simple management interface requiring simple programming logic in its generic methods, its implementation can show a better performance than the same functionality in a standard MBean.
A model MBean is a generic, configurable MBean which applications can use to instrument any resource dynamically. It is a dynamic MBean that has been implemented so that its management interface and its actual resource can be set programmatically. This allows any manager connected to a Java Dynamic Management agent to instantiate and configure a model MBean on the fly.
Model MBeans allow management applications to make resources manageable at runtime. The managing application must provide a compliant management interface for the model MBean to expose. It must also specify the target objects that actually implement the resource. Once it is configured, the model MBean will pass any management requests to the target objects and handle the result.
In addition, the model MBean provides a set of mechanisms for how management requests and their results are handled. For example, caching can be performed on attribute values. The management interface of a model MBean is augmented by descriptors which contain attributes for controlling these mechanisms.
The code samples in this topic are taken from the files in the ModelMBean example directory in the main examplesDir (see "Directories and Classpath" in the preface).
Contents:
"The RequiredModelMBean Class" gives an overview of model MBeans.
"Model MBean Metadata" explains how we can describe a model MBean' management interface.
"The Target Object(s)" describes how a model MBean is associated with its resource.
"Creating the Model MBean" shows how to instantiate and register a model MBean.
"Running the Model MBean Example" shows how an agent interacts with a model MBean.
The required Model MBean is mandated by the JMX specification for all compliant implementations. It is a dynamic MBean which lacks any predefined management interface. It contains a generic implementation which will transmit management request on it management interface to the target objects that define its managed resource.
The name of the required model MBean class is the same for all JMX-compliant implementation. Its full package and class name is javax.management.modelmbean.RequiredModelMBean. By instantiating this class, any application may use model MBeans.
In order to be useful, the instance of the required model MBean must be given a management interface and the target object of the management resource. In addition, the model MBean metadata must contain descriptors for configuring how the model MBean will respond to management requests. We will cover these steps in subsequent sections.
The MBean server does not make any special distinction for model MBeans. Internally they are treated as the dynamic MBeans that they are, and all of the model MBean's internal mechanisms and configurations are completely transparent to a management application. Like all other managed resources in the MBean server, the resources available through the model MBean can only be accessed through the attributes and operations defined in the management interface.
The metadata of a any MBean is the description of its management interface. The metadata of the model MBean is described by an instance of the ModelMBeanInfo class, which extends the MBeanInfo class.
Like all other MBeans, the metadata of a model MBean contains the list of attributes, operations, constructors, and notifications of the management interface. Model MBeans also describe their target object and their policies for accessing the target object. This information is contained in an object called a descriptor, defined by the Descriptor interface and implemented in the DescriptorSupport class.
There is one overall descriptor for a model MBean instance and one descriptor for each element of the management interface, that is for each attribute, operation, constructor, and notification. Descriptors are stored in the metadata object. As defined by the JMX specification all, of the classes for describing elements are extended so that they contain a descriptor. For example, the ModelMBeanAttributeInfo extends the MBeanAttributeInfo and defines the methods getDescriptor and getDescriptor.
A descriptor is a set of named field and value pairs. Each type of metadata element has a defined set of fields that are mandatory, and users are free to add others. The field names reflect the policies for accessing target objects, and their values determine the behavior. For example the descriptor of an attribute contains the fields currencyTimeLimit and lastUpdatedTimeStamp which are used by the internal caching mechanism when performing a get or set operation.
In this way, model MBeans are manageable as any other MBean, but applications which are aware of model MBeans may interact with the additional features which they provide. The JMX specification defines the names of all required descriptor fields for each of the metadata element, and for the overall descriptor. The field names are also documented in the Javadoc API for the ModelMBean*Info classes.
In our example, our application defines a subroutine to build all descriptors and metadata objects which are needed to define the management interface of the model MBean.
private void buildModelMBeanInfo( ObjectName inMbeanObjectName, String inMbeanName) { try { // Create the descriptor and ModelMBeanAttributeInfo // for the 1st attribute // Descriptor stateDesc = new DescriptorSupport(); stateDesc.setField("name","State"); stateDesc.setField("descriptorType","attribute"); stateDesc.setField("displayName","MyState"); stateDesc.setField("getMethod","getState"); stateDesc.setField("setMethod","setState"); stateDesc.setField("currencyTimeLimit","20"); dAttributes[0] = new ModelMBeanAttributeInfo( "State", "java.lang.String", "State: state string.", true, true, false, stateDesc); [...] // create descriptors and ModelMBean*Info for // all attributes, operations, constructors // and notifications // Create the descriptor for the whole MBean // mmbDesc = new DescriptorSupport( new String[] { ("name="+inMbeanObjectName), "descriptorType=mbean", ("displayName="+inMbeanName), "log=T", "logfile=jmxmain.log", "currencyTimeLimit=5"}); // Create the ModelMBeanInfo for the whole MBean // private String dClassName = "TestBean"; private String dDescription = "Simple implementation of a test app Bean."; dMBeanInfo = new ModelMBeanInfoSupport( dClassName, dDescription, dAttributes, dConstructors, dOperations, dNotifications); dMBeanInfo.setMBeanDescriptor(mmbDesc); } catch (Exception e) { echo("\nException in buildModelMBeanInfo : " + e.getMessage()); e.printStackTrace(); } } |
The object instance which actually embodies the behavior of the managed resource is called the target object. The last step of creating a model MBean is to give the MBean skeleton and its defined management interface a reference to the target object. Thereafter, management requests can be handled by model MBean, which will forward them to the target object and handle the response.
The following code example implements the TestBean class which is the simple managed resource in our example. Its methods provide the implementation for two attributes and one operation.
public class TestBean implements java.io.Serializable { // Constructor // public TestBean() { echo("\n\tTestBean Constructor Invoked: State " + state + " nbChanges: " + nbChanges + " nbResets: " + nbResets); } // Getter and setter for the "State" attribute // public String getState() { echo("\n\tTestBean: getState invoked: " + state); return state; } public void setState(String s) { state = s; nbChanges++; echo("\n\tTestBean: setState to " + state + " nbChanges: " + nbChanges); } // Getter for the read-only "NbChanges" attribute // public Integer getNbChanges() { echo("\n\tTestBean: getNbChanges invoked: " + nbChanges); return new Integer(nbChanges); } // Method of the "Reset" operation // public void reset() { echo("\n\tTestBean: reset invoked "); state = "reset initial state"; nbChanges = 0; nbResets++; } // Other public method; looks like a getter, // but no NbResets attribute is defined in // the management interface of the model MBean // public Integer getNbResets() { echo("\n\tTestBean: getNbResets invoked: " + nbResets); return new Integer(nbResets); } // Internals // private void echo(String outstr) { System.out.println(outstr); } private String state = "initial state"; private int nbChanges = 0; private int nbResets = 0; } |
By default, the model MBean handles a managed resource that is contained in one object instance. This target is specified through the setManagedResource method defined by the ModelMBean interface. The resource can encompass several programmatic objects because individual attributes or operations can be handled by different target objects. This behavior is configured through the optional targetObject and targetType descriptor fields of each attribute or operation.
In our example, one of the operations is handled by an instance of the TestBeanFriend class. In the definition of this operation's descriptor, we set this instance as the target object. We then create the operation's ModelMBeanOperationInfo with this descriptor and add it to the list of operations in the metadata for our model MBean.
MBeanParameterInfo[] params = null; Descriptor getNbResetsDesc = new DescriptorSupport(new String[] { "name=getNbResets", "class=TestBeanFriend", "descriptorType=operation", "role=operation"}); TestBeanFriend tbf = new TestBeanFriend(); getNbResetsDesc.setField("targetObject",tbf); getNbResetsDesc.setField("targetType","objectReference"); dOperations[1] = new ModelMBeanOperationInfo( "getNbResets", "getNbResets(): get number of resets performed", params , "java.lang.Integer", MBeanOperationInfo.INFO, getNbResetsDesc); |
In order to insure coherence in an agent application, you should define the target object of an MBean before you expose it for management. This implies that you should call the setManagedResource method before registering the model MBean in the MBean server.
The following code example show how our application creates the model MBean. First it calls the subroutine give in Example 3-1 to build the descriptors and management interface of our model MBean. Then it instantiates the required model MBean class with this metadata. Finally it creates and sets the managed resource object before registering the model MBean.
ObjectName mbeanObjectName = null; String domain = server.getDefaultDomain(); String mbeanName = "ModelSample"; try { mbeanObjectName = new ObjectName( domain + ":type=" + mbeanName); } catch (MalformedObjectNameException e) { e.printStackTrace(); System.exit(1); } // Create the descriptors and ModelMBean*Info objects // of the management interface // ModelMBeanInfo dMBeanInfo = null; buildModelMBeanInfo( mbeanObjectName, mbeanName ); try { // Instantiate javax.management.modelmbean.RequiredModelMBean RequiredModelMBean modelmbean = new RequiredModelMBean( dMBeanInfo ); // Associate it with the resource (a TestBean instance) modelmbean.setManagedResource( new TestBean(), "objectReference"); // register the model MBean in the MBean server server.registerMBean( modelmbean, mbeanObjectName ); } catch (Exception e) { echo("\t!!! ModelAgent: Could not create the model MBean !!!"); e.printStackTrace(); System.exit(1); } |
Our model MBean is then available for management operations and remote requests, just like any other registered MBean.
The examplesDir/ModelMBean directory contains the TestBean.java file which is the target object of the sample model MBean. This directory also contains a simple notification listener class and the agent application, ModelAgent, which instantiates, configures and manages a model MBean.
The model MBean itself is given by the RequiredModelMBean class defined in the javax.management.modelmbean package provided in the runtime jar file (jdmkrt.jar) of the Java Dynamic Management Kit.
Compile all files in this directory with the javac command. For example, on the Solaris platform, you would type:
$ cd examplesDir/ModelMBean/ $ javac -classpath classpath *.java |
To run the example, launch the agent class with the following command:
$ java -classpath classpath ModelAgent |
Type return when the application pauses to step through the example. The agent application handles all input and output in this example and gives us a view of the MBean at runtime.
We can then see the result of managing the resource through its exposed attributes and operations. The agent also instantiates and registers a listener object for attribute change notifications sent by the model MBean. You can see the output of this listener whenever it receives a notification, after the application has called one of the attribute setters.