Like all other MBean types, model MBeans must be created and registered with the MBean server, and, as with dynamic MBeans, the management interface of your resource is exposed through metadata classes. The similarities end there.
Every compliant JMX implementation must ship a class called
RequiredModelMBean
. This class is instantiated and
registered with the MBean server, and it implements several
interfaces (including DynamicMBean
) that make the
life of the instrumentation developer easier. But what is it that
makes a model MBean tick? What makes a model MBean work and live up
to its reputation (which we discussed in the previous section)? The
rest of this section is devoted to answering those questions.
The key difference between
instrumenting
a resource as a model MBean versus as a dynamic MBean
is the Descriptor
interface. A class implementing
this interface describes certain properties of the MBean to the agent
level of the JMX architecture, other MBeans, and management
applications. Each descriptor contains one or
more fields, which have corresponding
Object
values. Think of a field as a property of
the MBean that gives some other party in the system (usually a
management application) a little
“nugget” of information about the
MBean or one of its features (such as an attribute or operation). Of
course, in order to exploit the full power of a descriptor, the
management application must know about model MBeans. If the
management application knows nothing of model MBeans, it simply
treats the MBean as it would any other. Unfortunately, it is then
unable to exploit the additional metadata contained in the
descriptor. Example 4-1 shows the
Descriptor
interface.
Example 4-1. The Descriptor interface
public interface Descriptor extends java.io.Serializable { public Object getFieldValue(String fieldName) throws RuntimeOperationsException; public void setField(String fieldName, Object fieldValue) throws RuntimeOperationsException; public String[] getFields( ); public String[] getFieldNames( ); public Object[] getFieldValues(String[] fieldNames); public void removeField(String fieldName); public void setFields(String[] fieldNames, Object[] fieldValues) throws RuntimeOperationsException; public Object clone( ) throws RuntimeOperationsException; public boolean isValid( ) throws RuntimeOperationsException; }
As you can see from Example 4-1, the
Descriptor
interface revolves around the idea of a
field. A field
is a
name/value pair in which the name is a String
that
contains the name of the field and the value is an
Object
that is the value of the field.
Fields have two uses. First, field values are used internally by the
JMX implementation—for example, to determine when to retrieve
the value of an attribute from the internal cache or when to invoke
the getter for that attribute. The second use of
Descriptor
fields is to provide more information
to the agent level or a management application about an MBean or one
of its features. This information can then be exploited by any agent
or management application that is aware of model MBeans. We will
discuss these two types of fields separately, starting with those
fields that are used by the JMX implementation (which, for the
purposes of this discussion, is the JMX RI). Among the fields used by
the JMX RI are several that are required, which will be pointed out
when they are presented. None of the fields used by the agent level
or management applications are required for JMX compliance, but some
of those fields have constraints on the possible values they may
have. These will also be pointed out when they are presented.
The fields used by the JMX RI are:
class
currencyTimeLimit
default
descriptorType
(required)export
getMethod
log
logFile
name
(required)persistPeriod
persistPolicy
role
setMethod
severity
value
visibility
All of these fields are predefined by the JMX specification and are considered to be reserved. They are discussed in detail in the sections that follow.
This field
is
not required and applies only to operations. The value of this field
is the string representation of the Class
object
of the managed resource. For example, if an instance of the
application class Queue
is instrumented as a model
MBean, the value of this field would be
"sample.model.Queue"
. If the object is an
Integer
, the string representation of its
Class
object would be
"java.lang.Integer"
. Remember, the string
representation of a Class
object is always fully
qualified.
Example: "class=java.lang.Integer"
This field
is
not required and applies to attributes and operations. The value of
this field is a String
containing the number of
seconds that the value
field of an attribute or
the lastReturnedValue
field of an operation is
valid. Each time an attribute’s getter is called,
the value
field is updated with the latest value
for the attribute, and the lastUpdatedTimeStamp
field is set to the current system time.
Once the number of seconds specified by
currencyTimeLimit
plus the value of
lastUpdatedTimeStamp
exceeds the current system
time, the attribute’s value is considered
stale. At any time before that, however, when
the attribute’s value is requested, the
value
field is simply returned to the caller.
By the same token, when an operation is invoked, the
lastReturnedValue
field is updated and
lastReturnedTimeStamp
is set to the current system
time. Once the number of seconds specified by
currentTimeLimit
plus the value of
lastReturnedTimeStamp
exceeds the current system
time, the lastReturnedValue
is considered stale
and the operation is invoked.
This is how caching is performed in the JMX RI. It minimizes the impact of the management infrastructure on application performance for attributes and operations whose values and returned values, respectively, do not necessarily need to be accessed in real time.
If the value of this field is 0
, the getter for
the attribute is always called. If the value of this field is
-1
, the attribute’s value is
never considered to be stale.
Example: "currencyTimeLimit=5"
This field is not required and applies only to
attributes. The value of this field is a reference to an object of
the same type as the attribute. For example, suppose that the
attribute’s type is Float
. The
value of default
would then be a
Float
object that contains the default value.
This value is returned only if no getMethod
field
is defined. If a getMethod
is defined, this field
is ignored by the RI. This field can be set only by using the
setField( ) method of the
Descriptor
object.
This field is required and applies to the MBean,
attributes, operations (including constructors), and notifications.
The value of this field is a String
that contains
one of the following fixed values:
Any other value will result in an exception being thrown when you attempt to register the model MBean with the MBean server.
Example: "descriptorType=attribute"
The meaning
of
this field is somewhat vague, as it relates to the export policy of
an MBean in a distributed environment (and the distributed services
level of the JMX architecture has yet to be specified). The value of
the field is the fully qualified class name of an object that is
capable of making the MBean locatable—that is, an object that
allows the MBean to be found in a distributed environment. If the
MBean is not locatable, or if this behavior is not supported, the
value of this field should be set to F
.
Example: "export=F"
This field
is
not required and applies only to attributes. Although it is not
required, if you want to be able to access the value of an attribute,
this field is a must. If this field is not provided, the MBean server
tries to use the default
field value or the
value
field value to provide a value. The value of
getMethod
is a String
that
contains the name of the method on the managed resource that acts as
the getter for the attribute.
Example: "getMethod=isQueueFull"
This field
is
not required and applies only to notifications in the current RI. The
value of this field is a Boolean
object that
contains true
if notifications are to be logged or
false
if they are not. If you want to set this
field value to a Boolean
object, you must use the
setField( ) method of the
Descriptor
object. The value of this field may
also be T
for true
or
F
for false
.
Example: "log=T"
This field
is
not required and applies only to notifications in the current RI.
However, this field is required if log
is set to
true
. If log
is
true
but the logFile
field
either is not set or is set to an invalid value, no logging will be
performed. The value of this field is a String
that contains the full path to the log file to which notifications
will be logged. If the value of logFile
is not
fully qualified, the log file will be written to the current
directory.
Example: "logFile=/usr/home/steve/jmxlog"
This field
is
required and applies to the MBean, attributes, operations (including
constructors) and notifications. The value of this field is a
String
that must match exactly what is passed as
the name parameter to the appropriate metadata
constructor. For example, suppose we have an attribute called
WorkFactor
. When we create the
ModelMBeanAttributeInfo
object, we must pass
WorkFactor
as the name
parameter (which happens to be the first parameter) to the
constructor.
Example: "name=WorkFactor"
Tip
The JMX specification states that this field’s value is case-sensitive. However, in the JMX 1.0 RI, this does not appear to be the case. When instrumenting your resources, be careful to ensure that you match the case between this field and the name parameter of the metadata object. In future releases of the RI, case-sensitivity may be in place, causing your instrumentation to break.
This field
is
not required and applies to the MBean and attributes. The value of
this field is a String
containing the number of
seconds that should elapse before the attribute value is written to
persistent storage. The value of this field may also be a reference
to an Integer
object. If you want to set this
field value to an Object
reference, you must use
the setField( ) method of the
Descriptor
object. This field is valid only if
persistPolicy
is set to OnTimer
or NoMoreOftenThan
; otherwise, it is ignored.
Example: "persistPeriod=10"
This field
is
not required and applies to the MBean and attributes. The value of
this field is a String
that contains one of the
following values:
-
Never
-
OnTimer
The attribute value is persisted whenever the number of seconds specified by the
persistPeriod
field expires.-
OnUpdate
The attribute value is persisted whenever the attribute’s value changes.
-
NoMoreOftenThan
The attribute value is persisted only when the minimum number of seconds that should expire, as specified by the
persistPeriod
field, have elapsed.
Example: "persistPolicy=OnTimer"
This field is not required and applies only to operations. The following values for this field are currently recognized by the JMX RI:
Example: "role=getter"
This field
is
not required and applies only to attributes. Although it is not
required, if you want to be able to modify the value of an attribute,
this field is a must. The value of setMethod
is a
String
that contains the name of the method on the
managed resource that acts as the setter for the attribute.
Example: "setMethod=setQueueSize"
This field
is
not required and applies only to notifications. The JMX specification
defines seven numeric String
values, ranging from
0
to 6
, as shown in Table 4-1.
You may create your own values for severity
, but
consider those summarized in Table 4-1 to be
reserved. This helps to ensure compatibility between vendors.
Example: "severity=3"
This field
is
not required and applies only to attributes. This field acts as a
cache for the current value of the attribute. Each time an
attribute’s value is accessed or changed, this field
is updated to reflect the current value. This field can be used in
conjunction with currencyTimeLimit
to minimize the
impact of instrumentation on application performance, by acting as a
cache for get requests of the attribute’s current
value. When currencyTimeLimit
expires, the next
get request for the attribute invokes the
attribute’s getter and the value is updated. If you
want to set this field value to an Object
reference, you must use the setField( ) method
of the Descriptor
object.
Example: "value=SomeStringValue"
This field
is
not required and provides a built-in abstraction mechanism for
MBeans. There are four predefined values, ranging from
1
to 4
. At the least abstract
level, 1
, the MBean is nearly always visible to
any management application. At the greatest abstraction level,
4
, the MBean is the least visible. The concrete
meanings of “nearly always visible”
and “least visible” are not clear
in the specification. The meaning of this field will certainly
require some sort of agreement between instrumentation developers and
management application developers and will most likely be firmed up
in a future version (or maintenance release) of the specification.
Example: "visibility=1"
Three additional fields
are
predefined by the JMX specification and are considered to be
reserved: presentationString
,
iterable
, and messageID
.
The interpretation of the values of each of these fields is covered fairly well (if somewhat necessarily vaguely) in the JMX specification, and we won’t discuss them further here. Why not? Remember, one of the strengths of descriptors (and hence of model MBeans) is that they provide a richer set of metadata than is available for dynamic MBeans. The JMX specification provides general guidelines for interpreting predefined field values, but it does not constrain the model MBean instrumentation developer or the management application in terms of exactly how they are to interpret these values.
This openness of field values leads us into our next topic of discussion: user-defined field values. Are they allowed? Absolutely. The JMX specification does not prohibit the model MBean instrumentation developer, the agent level developer, or the management application developer from agreeing on a specific set of field values, as long as they do not conflict with the reserved field names we have already discussed.
So, how do you create a descriptor? As we have already seen, the
Descriptor
interface defines the contract between
the instrumentation level and any other level of the JMX
architecture. The JMX implementation must ship with at least one
concrete class that implements the Descriptor
interface. In the RI, this class is called
DescriptorSupport
. (It is a common pattern in the RI for
interfaces to be implemented by classes named by adding
“Support” to the name of the
interface.) DescriptorSupport
provides several
constructors, but we will only look at a few of the more interesting
ones here. Note that in the following discussion
we’ll use the terms descriptor,
Descriptor
, and
DescriptorSupport
synonymously (descriptor is a
generic term for the Descriptor
interface or a
class such as DescriptorSupport
that implements
it, respectively).
The easiest way to create a descriptor is to use the default constructor:
// . . . Descriptor desc = new DescriptorSupport( ); // . . .
What then? The next step is to set the fields that make up the descriptor, using the setField( ) method:
// . . . import javax.management.Descriptor; import javax.management.modelmbean.DescriptorSupport; // . . . Descriptor desc = new DescriptorSupport( ); desc.setField("name", "WorkFactor"); desc.setField("descriptorType", "attribute"); desc.setField("getMethod", "getWorkFactor"); // . . .
DescriptorSupport
also provides three constructors
that let you do all of this at once. One lets you pass an XML-like
String
object that contains the field names and
values. Its signature is:
public DescriptorSupport(String inStr) throws MBeanException, RuntimeOperationsException, XMLParseException { // . . . }
This XML-like String
must be of the format:
<descriptor> <field name=fieldname1
value=fieldvalue1
></field> <field name=fieldname2
value=fieldvalue2
></field> . . . <field name=fieldnameN
value=fieldvalueN
></field> </descriptor>
where fieldname1
is the name of the first
field, fieldvalue1
is its corresponding
value, and so on. Let’s use the
WorkFactor
attribute described earlier to
demonstrate how to use the DescriptorSupport
constructor:
// . . . import javax.management.Descriptor; import javax.management.modelmbean.DescriptorSupport; // . . . String xmlString = "<descriptor>" + "<field name=name value=WorkFactor></field>" + "<field name=descriptorType value=attribute></field>" + "<field name=getMethod value=getWorkFactor></field>" + "</descriptor>"; Descriptor desc = new DescriptorSupport(xmlString); // . . .
Warning
If you think this XML String
is not well formed,
you are correct; notice that what appear to be XML attributes in the
field
tag are not surrounded by single or double
quotes. I am not certain if this is intentional—if
it’s simply an oversight, it will certainly be fixed
in a future version of the JMX RI. Regardless, this is how the JMX RI
behaves, so it deserves mention.
If you need to set a number of fields whose values are
String
objects, you can create a String
array and pass it
to another DescriptorSupport
constructor. This
constructor’s signature is:
public DescriptorSupport(String[] fields) { // . . . }
The String
objects in the array are of the format
name
=value
,
where name
is the name of the field and
value
is its String
value. Using the WorkFactor
example again,
here’s how to create a descriptor with this
constructor:
// . . . import javax.management.Descriptor; import javax.management.modelmbean.DescriptorSupport; // . . . String[] fields = new String[] { "name=WorkFactor", "descriptorType=attribute", "getMethod=getWorkFactor" }; Descriptor desc = new DescriptorSupport(fields); // . . .
This constructor parses through the String
objects
in the array and sets each field accordingly.
The third and final constructor we’ll look at in
this section takes two arguments: a String
array
containing the names of the fields and an Object
array containing references to the objects that represent the field
values. The signature for this constructor is:
public DescriptorSupport(String[] fieldNames, Object[] fieldValues) throws RuntimeOperationsException { // . . . }
Note that the number of items in the String
array
must match the number of items in the Object
array, or none of the fields will be set for the descriptor you are
trying to create. Let’s again use the
WorkFactor
attribute from earlier to demonstrate
how to use this constructor:
// . . . import javax.management.Descriptor; import javax.management.modelmbean.DescriptorSupport; // . . . String[] fieldNames = new String[] { "name", "descriptorType", "getMethod" }; Object[] fieldValues = new Object[] { "WorkFactor", "attribute", "getWorkFactor" }; Descriptor desc = new DescriptorSupport(fieldNames, fieldValues); // . . .
We used an attribute here to demonstrate how to create descriptor objects, but the principles are exactly the same for an MBean, operation (including a constructor), or notification. The only differences are the allowable field names.
A final note about the DescriptorSupport
constructors: all of these constructors are valid ways to create
DescriptorSupport
objects, and none is preferable
over another. However, the following two constructors will be used
most often throughout the rest of this book:
Default constructor, setField( ) approach
String
array parameter constructor
Every attempt will be made to give these two constructors equal treatment in the examples that follow. However, bear in mind that you are free to use any of the constructors (that is, after all, why they are there) as your needs dictate.
By now you should be familiar with the
DescriptorSupport
constructors available to you to
create descriptors for your MBeans, attributes, operations, and
notifications. While descriptors provide metadata to other levels of
the JMX architecture, as well as to management applications, we must
still provide the MBean server with metadata classes that satisfy the
DynamicMBean
behavior of our model MBeans. In the
next section, we will look at how to create the metadata classes that
are required to describe the management interface to the MBean
server.
As with dynamic MBeans,
every
feature of a model MBean, including the MBean itself, must have a
corresponding metadata class. Furthermore, for model MBeans, each
metadata class must contain a descriptor. In this section, we will
examine the metadata classes that are used to describe the
management interface of your managed
resource. You may notice a similarity between the layout of this
section and the corresponding section in Chapter 3. Recall that a model MBean implements the
DynamicMBean
interface, so it should be no
surprise that model MBean metadata classes resemble their dynamic
MBean counterparts. In fact, as we will see, each of the model MBean
metadata classes (with the exception of
ModelMBeanInfo
, which is an interface) extends the
corresponding dynamic MBean metadata class.
There are five metadata classes of interest:
-
ModelMBeanAttributeInfo
Each instance of this class describes one attribute of the MBean’s management interface.
-
ModelMBeanContructorInfo
Each instance of this class describes one of the MBean’s public constructors and may contain one or more
MBeanParameterInfo
instances (MBeanParameterInfo
was discussed in detail in the previous chapter).-
ModelMBeanOperationInfo
Each instance of this class describes one of the operations on the MBean’s management interface and may contain one or more
MBeanParameterInfo
instances.-
ModelMBeanNotificationInfo
Each instance of this class describes one group of notifications emitted by the MBean.
-
ModelMBeanInfoSupport
The top-level container of metadata. This class implements the
ModelMBeanInfo
interface, which is how the agent level and management applications access the metadata for a model MBean. Each MBean requires only one instance of this class to completely describe its management interface.
As mentioned earlier, each of these metadata classes (with the
exception of ModelMBeanInfo
) extends its dynamic
MBean counterpart. These relationships are shown in Figure 4-1 in UML notation.
Figure 4-1. UML diagram showing inheritance of model MBean metadata classes from dynamic MBean metadata classes
As shown in Figure 4-2, the relationships between model MBean metadata classes are similar to those between dynamic MBean metadata classes.
Figure 4-2. UML diagram showing the relationships between ModelMBeanInfoSupport and the other model MBean metadata classes
As with dynamic MBeans, the aggregation mechanism used in
MBeanInfoSupport
for each MBean feature is an
array. As we saw in Figure 4-1, all of the model
MBean metadata classes extend their dynamic MBean counterparts and
are able to take advantage of the basic structure contained in them.
We will discuss each of the model MBean classes below.
MBeanParameterInfo
is discussed at length in the
previous chapter, so we will not cover it here.
Recall from our discussion of the
DescriptorSupport
class and the
Descriptor
interface that it does not matter
whether the descriptor fields are created in a
String
array and passed to the
DescriptorSupport
constructor or created with the
setField( ) method of
Descriptor
. Both approaches will be used in the
examples throughout the rest of this chapter to demonstrate that they
are equally valid.
All but one of
the
essential properties that must be set for a model MBean attribute are
the same as those that must be set for a dynamic MBean attribute, so
they will not be discussed here. The lone exception is the descriptor
that must be set for a model MBean attribute. Recall from the
previous chapter that there are two constructors that are used to set
the essential properties for an MBeanAttributeInfo
instance. One of these constructors uses the Java reflection API, and
the other explicitly sets all of the essential properties. These same
properties (in addition to the descriptor) exist on
ModelMBeanAttributeInfo
, which inherits from
MBeanAttributeInfo
. Four constructors are
available to you on
ModelMBeanAttributeInfo
,
as shown in Example 4-2.
Example 4-2. The significant constructors of ModelMBeanAttributeInfo
// . . . public class ModelMBeanAttributeInfo extends MBeanAttributeInfo implements DescriptorAccess, Cloneable { // . . . public ModelMBeanAttributeInfo(String name, String description, Method getter, Method setter) throws javax.management.IntrospectionException { // . . . } public ModelMBeanAttributeInfo(String name, String description, Method getter, Method setter, Descriptor descriptor) throws javax.management.IntrospectionException { // . . . } public ModelMBeanAttributeInfo(String name, String type, String description, boolean isReadable, boolean isWritable, boolean isIs) { // . . . } public ModelMBeanAttributeInfo(String name, String type, String description, boolean isReadable, boolean isWritable, boolean isIs, Descriptor descriptor) { // . . . } // . . . }
Notice the emphasized lines in Example 4-2. The
second and fourth constructors are each passed a reference to a
Descriptor
object. The first and second constructors are otherwise identical, as
are the third and fourth. As mentioned earlier, every model MBean
attribute contains a Descriptor
object that
provides a richer set of metadata than that of the metadata class.
In the first and third constructors in Example 4-2,
a
default
descriptor is created by
ModelMBeanAttributeInfo
. The default descriptor
contains the following predefined fields:
descriptorType
displayName
iterable
name
Each of these fields (with the exception of
iterable
) was discussed in a previous section. For
two of these fields, the values are literal strings: the value for
descriptorType
is "attribute"
,
and the value for iterable
is
"F"
. The values for both
displayName
and name
are set to
the name parameter that was passed to the
constructor. Consider the following example, which results in a
descriptor whose name
and
displayName
fields are set to
"WorkFactor"
:
// . . .
ModelMBeanAttributeInfo[] attributeInfo = new ModelMBeanAttributeInfo[1];
attributeInfo[0] = new ModelMBeanAttributeInfo(
"WorkFactor",
"java.lang.Integer",
"Amount of work performed per work unit.",
true,
false,
false
);
// . . .
This example uses the third constructor from Example 4-2 (use of the first constructor requires more code). For more information on the use of the reflection API for this constructor, see Section 3.2.1.1 in the previous chapter.
Notice the second parameter to the
MBeanAttributeInfo
constructor in this example.
Even though the attribute type for
WorkFactor
is the fundamental type
int
, we pass
"java.lang.Integer"
to the
ModelMBeanAttributeInfo
constructor. There appears
to be an oversight in the JMX 1.0 RI: when the getter for an
attribute whose return value is a fundamental type is invoked, the
return value is wrapped in its corresponding JDK wrapper class. Thus,
even though the getter for WorkFactor
returns an
int
, the reflection API (which is what the JMX RI
uses under the hood to perform the invocation) wraps the return value
in a java.lang.Integer
. However, the RI does not
take this into account, and upon returning from the getter, the RI
checks to see if the return value matches what was expected.
Say we create the metadata using the proper value (notice parameter #2):
// . . . attributeInfo[0] = new ModelMBeanAttributeInfo( "WorkFactor", Integer.TYPE.getName( ), "Amount of work performed per work unit.", true, false, false ); // . . .
The RI looks at the return value from the getter (which has been
wrapped in a java.lang.Integer
), compares it to
what is in the metadata (which is the string representation of a
fundamental int
), declares the return value to be
bad, and throws an exception. This is clearly not what we
intended—to work around the problem, we have to specify the JDK
wrapper class name when we create the metadata class.
If you would like to create your own descriptor instead of using the
default descriptor, you can use the second and fourth constructors
shown in Example 4-2. The second constructor uses
Method
objects and the Java reflection API and
differs from the fourth constructor only in that regard. Use of this
constructor is otherwise identical to that of the corresponding
MBeanAttributeInfo
constructor, which was covered
in the previous chapter, and will not be presented here. The
following example shows how to create a
ModelMBeanAttributeInfo
object using the fourth
constructor for the read-only attribute
WorkFactor
:
// . . . Descriptor desc = new DescriptorSupport( ); desc.setField("name", "WorkFactor"); desc.setField("descriptorType", "attribute"); desc.setField("getMethod", "getWorkFactor"); ModelMBeanAttributeInfo[] attributeInfo = new ModelMBeanAttributeInfo[1]; attributeInfo[0] = new ModelMBeanAttributeInfo( "WorkFactor", "java.lang.Integer", "Amount of work performed per work unit.", true, false, false, desc ); // . . .
Notice that we create the Descriptor
object by
instantiating the DescriptorSupport
class, as we
discussed earlier in this chapter. We used the setField(
) method in this example, but there are many ways to set
the fields of the descriptor. The use of the other
DescriptorSupport
constructors was discussed at
length earlier in this
chapter.
All but
one
of the essential properties that must be set for a model MBean
constructor are the same as those that must be set for a dynamic
MBean constructor, so they will not be discussed here. The lone
exception is the descriptor that must be set for a model MBean
constructor. Recall from the previous chapter that there are two
constructors that are used to set the essential properties for an
MBeanConstructorInfo
instance. One of these
constructors uses the Java reflection API, and the other explicitly
sets all of the essential properties. These same properties (in
addition to the descriptor) exist on
ModelMBeanConstructorInfo
, which inherits from
MBeanConstructorInfo
. Four constructors are
available to you on
ModelMBeanConstructorInfo
, as shown in Example 4-3.
Example 4-3. The significant constructors of ModelMBeanConstructorInfo
public class ModelMBeanConstructorInfo extends MBeanConstructorInfo implements DescriptorAccess, Cloneable { // . . . public ModelMBeanConstructorInfo(String description, Constructor constructorMethod) { // . . . } public ModelMBeanConstructorInfo(String description, Constructor constructorMethod, Descriptor descriptor) { // . . . } public ModelMBeanConstructorInfo(String name, String description, MBeanParameterInfo[] signature) { // . . . } public ModelMBeanConstructorInfo(String name, String description, MBeanParameterInfo[] signature, Descriptor descriptor) { // . . . } // . . . }
Notice the emphasized lines in Example 4-3. The
second and fourth constructors are each passed a reference to a
Descriptor
object. The first and second
constructors are otherwise identical, as are the third and fourth.
In the first and third constructors in Example 4-3,
a default descriptor containing the following predefined fields is
created by
ModelMBeanConstructorInfo
:
descriptorType
displayName
name
role
Each of these fields was discussed in a previous section. For two of
these fields, the values are literal strings: the value for
descriptorType
is "operation"
,
and the value for role
is
"constructor"
. The values for both
displayName
and name
are set to
the name parameter that was passed to the
constructor. Consider the following example:
// . . . ModelMBeanConstructorInfo[] constructorInfo = new ModelMBeanConstructorInfo[1]; constructorInfo[0] = new ModelMBeanConstructorInfo( "DefaultConstructor", "The default constructor", new MBeanParameterInfo[0] ); // . . .
This example uses the third constructor from Example 4-3. The
ModelMBeanConstructorInfo
object created here
results in a descriptor whose name
and
displayName
fields are set to
"DefaultConstructor"
.
If you would like to create your own descriptor, you can use the
second and fourth constructors shown in Example 4-3.
The second constructor uses Constructor
objects
and the Java reflection API and differs from the fourth constructor
only in that regard. The following example shows how to create a
ModelMBeanConstructorInfo
object using the fourth
constructor for an MBean’s default constructor:
// . . .
Descriptor desc = new DescriptorSupport( );
desc.setField("name", "DefaultConstructor");
desc.setField("descriptorType", "operation");
desc.setField("role", "constructor");
desc.setField("displayName", "The Default Constructor");
ModelMBeanConstructorInfo[] constructorInfo = new ModelMBeanConstructorInfo[1];
constructorInfo[0] = new ModelMBeanConstructorInfo(
"DefaultConstructor",
"The default constructor",
new MBeanParameterInfo[0],
desc
);
Notice that in this example the name
and
displayName
fields do not have the same value, as
is the case when a default descriptor is created for a
ModelMBeanConstructorInfo
object. This is one
advantage of creating your own descriptor.
What if you simply want to expose all of the public constructors for
your MBean? This method was covered thoroughly in the previous
chapter; however, it bears repeating here, because this is by far the
simplest way to create ModelMBeanConstructorInfo
objects.
In the following example, we create a descriptor and pass it
explicitly to the
ModelMBeanConstructorInfo
’s
second constructor:
// . . . Constructor[] constructors = this.getClass().getConstructors( ); ModelMBeanConstructorInfo[] constructorInfo = new ModelMBeanConstructorInfo[constructors.length]; for (int aa = 0; aa < constructors.length; aa++) { Descriptor desc = new DescriptorSupport( new String[] { ("name=" + constructors[aa].getName( )), "descriptorType=operation", "role=constructor" }; ); constructorInfo[aa] = new ModelMBeanConstructorInfo( "Constructs a Basic MBean.", // description constructors[aa] // java.lang.reflect.Constructor desc ); } // . . .
Notice that the displayName
field has been omitted
from this example. Recall from our discussion of the required
descriptor fields that only name
and
descriptorType
are required, so we are free to
omit
displayName
.
All but
one
of the essential properties that must be set for a model MBean
operation are exactly the same as those that must be set for a
dynamic MBean operation, so they will not be discussed here. The lone
exception is the descriptor that must be set for a model MBean
operation. Recall from the previous chapter that there are two
constructors that are used to set the essential properties for an
MBeanOperationInfo
instance. One of these
constructors uses the Java reflection API, and the other explicitly
sets all of the essential properties. These same properties (in
addition to the descriptor) exist on
ModelMBeanOperationInfo
, which inherits from
ModelMBeanOperationInfo
. There are four
constructors
of interest on
ModelMBeanOperationInfo
, as shown in Example 4-4.
Example 4-4. The significant constructors of ModelMBeanOperationInfo
public class ModelMBeanOperationInfo extends MBeanOperationInfo implements DescriptorAccess { // . . . public ModelMBeanOperationInfo(String description, Method operationMethod) { // . . . } public ModelMBeanOperationInfo (String description, Method operationMethod, Descriptor descriptor) } // . . . } public ModelMBeanOperationInfo(String name, String description, MBeanParameterInfo[] signature, String type, int impact) { // . . . } public ModelMBeanOperationInfo(String name, String description, MBeanParameterInfo[] signature, String type, int impact, Descriptor descriptor) { // . . . } // . . . }
Notice the emphasized lines in Example 4-4. The
second and fourth constructors are each passed a reference to a
Descriptor
object. The first and second
constructors are otherwise identical, as are the third and fourth.
In the first and third constructors in Example 4-4,
a default descriptor containing the following predefined fields
is created by
ModelMBeanOperationInfo
:
descriptorType
displayName
name
role
Each of these fields was discussed in a previous section. For two of
these fields, the values are literal strings: the value for
descriptorType
is "operation"
,
and the value for role
is
"operation"
. The values for both
displayName
and name
are set to
the name parameter that was passed to the
constructor. Consider the following example:
// . . . ModelMBeanOperationInfo[] operationInfo = new ModelMBeanOperationInfo[1]; operationInfo[0] = new ModelMBeanOperationInfo( "reset", "Resets the state of this MBean.", new MBeanParameterInfo[0], Void.TYPE.getName( ), MBeanOperationInfo.ACTION ); // . . .
This example uses the third constructor from Example 4-4. The
ModelMBeanOperationInfo
object created here
results in a descriptor whose name
and
displayName
fields are set to
"reset"
.
If you would like to create your own descriptor, you can use the
second and fourth constructors shown in Example 4-4.
The second constructor uses Constructor
objects
and the Java reflection API and differs from the fourth constructor
only in that regard. Use of this constructor is otherwise identical
to that of the corresponding constructor of
MBeanOperationInfo
—covered in the previous
chapter—and will not be presented here. The following example
shows how to create a ModelMBeanOperationInfo
object using the fourth constructor:
// . . .
Descriptor desc = new DescriptorSupport(
new String[] {
"name=reset",
"descriptorType=operation",
"role=operation",
("class=" + this.getClass().getName( ))
}
);
ModelMBeanOperationInfo[] operationInfo = new ModelMBeanOperationInfo[1];
operationInfo[0] = new ModelMBeanOperationInfo(
"reset",
"Resets the state of this MBean.",
new MBeanParameterInfo[0],
Void.TYPE.getName( ),
MBeanOperationInfo.ACTION,
desc
);
The only difference between the previous two examples is that we
created our own descriptor in the second. There are several
advantages to creating your own descriptors, not the least of which
is improved code readability. Notice that the
class
field was set to
this.getClass().getName( )
. This information is
used by RequiredModelMBean
when making the actual
invocation of the reset( )
method.
Warning
At the time of this writing, there is a bug in the JMX 1.0 RI when
allowing ModelMBeanOperationInfo
to create a
default descriptor for a management operation that causes any
invocation of that operation to fail. If you create a descriptor and
pass it to
ModelMBeanOperationInfo
’s
constructor, you will avoid this problem.
All but one
of
the essential properties that must be set for a model MBean
notification are exactly the same as those that must be set for a
dynamic MBean notification, so they will not be discussed here. The
lone exception is the descriptor that must be set for a model MBean
notification. Recall from the previous chapter that there is a single
constructor that is used to set the essential properties for an
MBeanNotificationInfo
instance. These same
properties (in addition to the descriptor) exist on
ModelMBeanNotificationInfo
, which inherits from
MBeanNotificationInfo
. There are two
constructors of interest on
ModelMBeanNotificationInfo
, as shown in Example 4-5.
Example 4-5. The significant constructors of ModelMBeanNotificationInfo
public class ModelMBeanNotificationInfo extends MBeanNotificationInfo
implements DescriptorAccess, Cloneable {
// . . .
public ModelMBeanNotificationInfo(String[] notifTypes,
String name,
String description) {
// . . .
}
public ModelMBeanNotificationInfo(String[] notifTypes,
String name,
String description,
Descriptor descriptor) {
// . . .
}
// . . .
}
Notice the emphasized line in Example 4-5. The
second constructor is passed a reference to a
Descriptor
object, but the two constructors are
otherwise identical.
In the first constructor, a default descriptor containing the
following predefined
fields is created by
ModelMBeanNotificationInfo
:
descriptorType
displayName
name
severity
Each of these fields was discussed in a previous section. For two of
these fields, the values are literal strings: the value for
descriptorType
is
"notification"
, and the value for
severity
is "5"
(i.e.,
Warning). The values for both displayName
and
name
are set to the name
parameter that was passed to the constructor. Consider the following
example, which uses the same notification types that we discussed in
the previous chapter (refer to Section 3.2.1.5 for more
information):
// . . . String[] notificationTypes = new String[] { "sample.Queue.stalled.queueFull", "sample.Queue.stalled.queueEmpty" }; ModelMBeanNotificationInfo[] notificationInfo = new ModelMBeanNotificationInfo[1]; notificationInfo[0] = new ModelMBeanNotificationInfo( notificationTypes, "StalledQueueNotifications", "Potential stall notifications emitted by the Queue." ); // . . .
In this example, the first constructor of
ModelMBeanNotificationInfo
is called, resulting in
a default descriptor whose name
and
displayName
fields are set to
"StalledQueueNotifications"
.
If you would like to create your own descriptor, use the second
constructor. The only difference between the two constructors is the
addition of a Descriptor
parameter. Creating a
descriptor is very straightforward, as we have already seen. The
following example shows how to use the second constructor, which
allows you to pass a Descriptor
:
// . . . String[] notificationTypes = new String[] { "sample.Queue.stalled.queueFull", "sample.Queue.stalled.queueEmpty" }; ModelMBeanNotificationInfo[] notificationInfo = new ModelMBeanNotificationInfo[1]; Descriptor desc = new DescriptorSupport( new String[] { "name=StalledQueueNotifications", "descriptorType=notification", "severity=3" }; ); notificationInfo[0] = new ModelMBeanNotificationInfo( notificationTypes, "StalledQueueNotifications", "Potential stall notifications emitted by the Queue.", desc ); // . . .
In this example, a descriptor is created for potential stall
conditions of the Queue
class. The severity field
is set to "3"
, indicating that a stall condition
is very serious and may require immediate operator intervention.
Other than the creation of the descriptor, the previous two examples
are effectively
identical.
All but one
of
the essential properties that must be set for a model MBean are the
same as those that must be set for a dynamic MBean, so they will not
be discussed here. The lone exception is the descriptor that must be
set for a model MBean. Recall from the previous chapter that there is
a single constructor that is used to set the essential properties for
an MBeanInfo
instance. These same properties (in
addition to the descriptor) exist on
ModelMBeanInfo
. The difference between
MBeanInfo
and ModelMBeanInfo
is
that ModelMBeanInfo
is an interface and cannot be
instantiated.
There is a class in the RI called
ModelMBeanInfoSupport
that provides an
implementation of the ModelMBeanInfo
interface and
constructors to create it in exactly the same fashion as the other
metadata classes. These
constructors are shown in Example 4-6.
Example 4-6. The significant constructors of ModelMBeanInfoSupport
public class ModelMBeanInfoSupport extends MBeanInfo
implements ModelMBeanInfo, java.io.Serializable {
// . . .
public ModelMBeanInfoSupport(String className,
String description,
ModelMBeanAttributeInfo[] attributes,
ModelMBeanConstructorInfo[] constructors,
ModelMBeanOperationInfo[] operations,
ModelMBeanNotificationInfo[] notifications) {
// . . .
}
public ModelMBeanInfoSupport(String className,
String description,
ModelMBeanAttributeInfo[] attributes,
ModelMBeanConstructorInfo[] constructors,
ModelMBeanOperationInfo[] operations,
ModelMBeanNotificationInfo[] notifications,
Descriptor mbeandescriptor) {
// . . .
}
// . . .
}
In the first constructor, a default descriptor is created by
ModelMBeanInfoSupport
. The default descriptor
contains the predefined fields and values listed in Table 4-2.
Table 4-2. Fields and corresponding values for a model MBean default descriptor
Field |
Value |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Like the other model MBean metadata classes, the second constructor
of ModelMBeanInfoSupport
takes as its last
parameter a Descriptor
object. The following
example shows how to create a
ModelMBeanInfoSupport
object using the second
constructor. This example also shows relevant portions of the
previous examples to provide you with some context.
// . . . ModelMBeanAttributeInfo[] attributeInfo = new ModelMBeanAttributeInfo[1]; // create attribute metadata ModelMBeanConstructorInfo[] constructorInfo = new ModelMBeanConstructorInfo[1]; // create constructor metadata ModelMBeanOperationInfo[] operationInfo = new ModelMBeanOperationInfo[1]; // create operation metadata ModelMBeanNotificationInfo[] notificationInfo = // create notification metadata ModelMBeanInfo mbeanInfo = new ModelMBeanInfoSupport( "ModeMBean", "A Model MBean", attributeInfo, constructorInfo, operationInfo, notificationInfo ); // . . .
As you can imagine, this example would be quite lengthy if all of the
code necessary to create the ModelMBeanInfo
object
were shown. However, we have already discussed at length how to
create the other metadata classes (with their optional descriptors),
so you should be adequately prepared to create
ModelMBeanInfo
objects.
DescriptorAccess
is a simple interface that
must be implemented by each of the metadata classes so that access to
the descriptor is available. The DescriptorAccess
interface is defined
as:
public interface DescriptorAccess { public Descriptor getDescriptor( ); public void setDescriptor(Descriptor inDescriptor); }
By implementing this interface, metadata classes provide a means to
access or even replace their existing descriptors. Recall from
earlier in this chapter, when we discussed how to create the metadata
classes, that each metadata class provides a default descriptor if
none is specified when the class is instantiated. At first glance, it
may appear that you must either create your own descriptor or put up
with the default descriptor. However, through the
DescriptorAccess
interface, you can create a
metadata object, access its descriptor, and modify or add fields to
it. Consider the following code snippet, where we create a
ModelMBeanAttributeInfo
object without specifying
a Descriptor
on the constructor call:
ModelMBeanAttributeInfo[] attributeInfo = new ModelMBeanAttributeInfo[1];
attributeInfo[0] = new ModelMBeanAttributeInfo(
"WorkFactor",
"java.lang.Integer",
"Amount of work performed per work unit.",
true,
false,
false
);
As you may recall, this will result in a default descriptor with
certain predefined fields. What if you want to modify or add fields
to the default descriptor? Because
ModelMBeanAttributeInfo
implements the
DescriptorAccess
interface, this is
straightforward. Suppose that after the previous code snippet
executes, we want to modify the displayName
property and add a persistPolicy
property.
Here’s what we would do:
// . . . ModelMBeanAttributeInfo[] attributeInfo = new ModelMBeanAttributeInfo[1]; attributeInfo[0] = new ModelMBeanAttributeInfo( "WorkFactor", "java.lang.Integer", "Amount of work performed per work unit.", true, false, false ); Descriptor desc = attributeInfo[0].getDescriptor( ); desc.setField("displayName", "Work Factor"); desc.setField("persistPolicy=never"); // . . .
The DescriptorAccess
interface gives you the
option of modifying a default descriptor. If you are happy with most
of the field values that are set with a default descriptor, this
approach may save you a few lines of code per metadata object.
Every compliant JMX implementation is required to implement a model
MBean called
RequiredModelMBean
.
The managed resource that wishes itself to be instrumented using the
RequiredModelMBean
(or possibly even another model
MBean, if the implementation provides more than the one concrete
model MBean) obtains a reference to the MBean server, and then a
reference to a new instance of the model MBean. Once the resource has
the model MBean reference, it uses the metadata classes to configure
the management interface it wants to expose. In this section, most of
the information comes straight from the JMX RI.
RequiredModelMBean
must implement at least three
interfaces in order to be compliant. These interfaces are discussed
below.
This interface must be implemented by every concrete model MBean and is defined as:
public interface ModelMBean extends DynamicMBean, PersistentMBean, ModelMBeanNotificationBroadcaster { public void setModelMBeanInfo(ModelMBeanInfo inModelMBeanInfo) throws MBeanException, RuntimeOperationsException; public void setManagedResource(Object mr, String mr_type) throws MBeanException, RuntimeOperationsException, InstanceNotFoundException, InvalidTargetObjectTypeException; }
The model MBean metadata and the resource to be managed are set
through this interface. Once the ModelMBeanInfo
object has been created, setModelMBeanInfo(
)
is called. This establishes the management
interface of the resource to be managed, which is set through a call
to setManagedResource(
)
. The second argument to
setManagedResource( ) is a
String
and must be one of the following predefined
values:
ObjectReference
Handle
IOR
EJBHandle
RMIReference
The only value we will use in the examples in this book is
ObjectReference
. We will see later in this chapter
how to use this interface.
We
discussed the
DynamicMBean
interface at length in the previous
chapter, so we won’t discuss it here. Note, however,
that every compliant concrete model MBean (such as
RequiredModelMBean
) must also implement
DynamicMBean
.
This interface provides a persistence mechanism for every model MBean and is defined as:
public interface PersistentMBean { public void load( ) throws MBeanException, RuntimeOperationsException, InstanceNotFoundException; public void store( ) throws MBeanException, RuntimeOperationsException, InstanceNotFoundException; }
Once a reference to a model MBean is obtained by the agent level, the state of the model MBean may be persisted or restored from a persistent store by invoking store( ) or load( ), respectively.
Get Java Management Extensions now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.