Tải bản đầy đủ - 0 (trang)
Example 3-3. Creating an instance of MBeanInfo

Example 3-3. Creating an instance of MBeanInfo

Tải bản đầy đủ - 0trang

attributeInfo[6] = new MBeanAttributeInfo(

"EndOfInput", Boolean.TYPE.getName(),

"Indicates if end-of-input has been signalled by all suppliers.",

true, false, true);

attributeInfo[7] = new MBeanAttributeInfo(

"NumberOfItemsProcessed", Long.TYPE.getName(),

"The number of items that have been removed from the Queue.",

true, false, false);

attributeInfo[8] = new MBeanAttributeInfo(

"AddWaitTime", Long.TYPE.getName(),

"No. Milliseconds spent waiting to add because Queue was full.",

true, false, false);

attributeInfo[9] = new MBeanAttributeInfo(

"RemoveWaitTime", Long.TYPE.getName(),

"No. milliseconds spent waiting to remove because Queue was

empty.",

true, false, false);

// Constructors

Class[] signature = {Integer.TYPE};

Constructor constructor = null;

MBeanConstructorInfo[] constructorInfo = new

MBeanConstructorInfo[1];

try {

constructor = this.getClass().getConstructor(signature);

constructorInfo[0] = new MBeanConstructorInfo(

"Custom constructor", constructor);

} catch (Exception e) {

e.printStackTrace();

}

// Operations

MBeanOperationInfo[] operationInfo = new MBeanOperationInfo[3];

MBeanParameterInfo[] parms = new MBeanParameterInfo[0];

operationInfo[0] = new MBeanOperationInfo(

"suspend", "Suspends processing of the Queue.",

parms, Void.TYPE.getName(), MBeanOperationInfo.ACTION);

operationInfo[1] = new MBeanOperationInfo(

"resume", "Resumes processing of the Queue.",

parms, Void.TYPE.getName(), MBeanOperationInfo.ACTION);

operationInfo[2] = new MBeanOperationInfo(

"reset", "Resets the state of this MBean.",

parms, Void.TYPE.getName(), MBeanOperationInfo.ACTION);

// Notifications

MBeanNotificationInfo[] notificationInfo = new

MBeanNotificationInfo[1];

String[] notificationTypes = new String[2];

notificationTypes[0] = NOTIF_STALLED_FULL;

notificationTypes[1] = NOTIF_STALLED_EMPTY;

notificationInfo[0] = new MBeanNotificationInfo(

notificationTypes,

"StalledQueueNotifications",

"Potential stall notifications emitted by the Queue."

);

// MBeanInfo

_MBeanInfo = new MBeanInfo(

"Queue",

"Queue MBean",

attributeInfo,



96



constructorInfo,

operationInfo,

notificationInfo

);

}

// . . .

}



In this example, 10 attributes, 1 constructor, 3 operations, and 2 notifications describe the

Queue as a managed resource. Notice the private variable _MBeanInfo, which is used to

hold an instance of the MBeanInfo object that contains the metadata for the Queue MBean.

This variable is returned by the getMBeanInfo() method(part of the DynamicMBean

interface) that is invoked by a management application to discover the management

interface of an MBean.

In this section, we looked at how to create all of the metadata classes necessary to fully

describe the management interface of a dynamic MBean. In the next section, we will take

a closer look at how the information in the MBeanInfo object must match up to the logic

in the implementation of the DynamicMBean interface.

3.2.2 Implementing the DynamicMBean Interface

Example 3-1 showed the definition of the DynamicMBean interface. In this section, we

will first look at some other support classes from the javax.management package that

are important to properly implementing dynamic MBeans. Then we will look at how to

write code to implement the methods on the dynamic MBean interface.

3.2.2.1 Attribute



This class is used to encapsulate a single attribute value. There are two important

properties of this class, name and value, which represent the attribute's name and value,

respectively. The following code shows the significant features of this class:

package javax.management;

// . . .

public class Attribute implements Serializable {

// . . .

public Attribute(String name, Object value) {

// . . .

}

// . . .

public String getName() {

// . . .

}

public Object getValue() {

// . . .

}

// . . .

}



97



There are three significant features to this class:

Constructor

When creating the class, pass in the name of the attribute and an object reference

for the attribute's value. If the attribute's type is one of the Java fundamental types,

wrap that fundamental type in the appropriate JDK wrapper class (e.g., if the type

is a char, wrap it in an Character object).

Getter for the attribute name

Returns the attribute's name.

Getter for the attribute's value

Returns the attribute's value.

3.2.2.2 AttributeList



Instances of this class are used to hold a List of Attribute instances. The following

code shows the significant features of AttributeList:

package javax.management;

import java.util.ArrayList;

// . . .

public class AttributeList extends ArrayList {

public AttributeList() {

}

// . . .

}

AttributeList inherits from ArrayList, which means that all of the methods you are

used to using for an ArrayList—such as get(), set(), and iterator()—are available on

AttributeList (see the JDK Javadoc for more information).



To create an instance of AttributeList, use:

AttributeList attributeList = new AttributeList();



To set the initial capacity of the internal ArrayList to 10, use:

AttributeList attributeList = new AttributeList(10);



To add an Attribute to the AttributeList, use:

Attribute attribute = new Attribute("QueueSize", new Integer(10));

AttributeList attributeList = new AttributeList();

attributeList.add(attribute);



98



AttributeList is an ArrayList, so there are two ways of getting at the contents of any

AttributeList instance. The first way is to use a java.util.Iterator object:

// assume attributeList is an instance of AttributeList

Iterator iter = attributeList.iterator();

while (iter.hasNext()) {

Attribute attribute = (Attribute)iter.next();

// do something with attribute. . .

}



The second way is to access the contents using an index. The code snippet below shows

how to walk through the contents of the AttributeList from beginning to end. However,

you can also access any member of the AttributeList directly by specifying its index

(as long as you don't specify an invalid index, in which case an

IndexOutOfBoundsException will be thrown).

// assume attributeList is an instance of AttributeList

for (int idx = 0; idx < attributeList.size(); idx++) {

Attribute attribute = (Attribute)attributeList.get(idx);

// do something with attribute. . .

}



Now, let's take the example of the Queue class from the sample application. We have

already looked at Queue's management interface and how we created the necessary

metadata classes to expose it. Here we will look at how we implement DynamicMBean on

the Queue class so that the management interface functionally corresponds to how it is

exposed.

The getMBeanInfo() method of the DynamicMBean interface is what ties the metadata to

the implementation of the DynamicMBean interface. This is a very simple method that

simply returns an MBeanInfo instance that contains the definition of the management

interface of the MBean. A management application uses getMBeanInfo() to discover what

the management interface looks like. But then what? Well, now that the management

interface is known, the management application uses the DynamicMBean interface to set

and get attribute values and invoke methods on the MBean.

Let's take a closer look at the methods of the DynamicMBean interface and how to

implement them.

3.2.2.3 getAttribute()



This method is used to retrieve a single attribute value and is defined as:

public Object getAttribute(String attribute)

throws AttributeNotFoundException,

MBeanException,

ReflectionException {

// . . .

}



99



Recall from Section 3.2.1 that every attribute must have a corresponding

MBeanAttributeInfo instance to describe it. One of the required parameters to both of

the constructors of MBeanAttributeInfo is name, which is the name of the attribute.

This name is passed to getAttribute() as a String. If this parameter does not match one of

the attribute names from the metadata, you must throw an

AttributeNotFoundException. The following example shows one such implementation.

When looking at this example, you may want to refer back to Example 3-3, which shows

the metadata created for the attributes of the Queue class, so you can see how the attribute

names there must match up with the code in getAttribute().

public class Queue extends Basic implements DynamicMBean {

// . . .

public Object getAttribute(String attributeName)

throws AttributeNotFoundException,

MBeanException,

ReflectionException {

Object ret = null;



}



if (attributeName.equals("QueueSize")) {

ret = new Integer(getQueueSize());

}

else if (attributeName.equals("NumberOfSuppliers")) {

ret = new Integer(getNumberOfSuppliers());

}

else if (attributeName.equals("NumberOfConsumers")) {

ret = new Integer(getNumberOfConsumers());

}

else if (attributeName.equals("QueueFull")) {

ret = new Boolean(isQueueFull());

}

else if (attributeName.equals("QueueEmpty")) {

ret = new Boolean(isQueueEmpty());

}

else if (attributeName.equals("Suspended")) {

ret = new Boolean(isSuspended());

}

else if (attributeName.equals("EndOfInput")) {

ret = new Boolean(isEndOfInput());

}

else if (attributeName.equals("NumberOfItemsProcessed")) {

ret = new Long(getNumberOfItemsProcessed());

}

else if (attributeName.equals("AddWaitTime")) {

ret = new Long(getAddWaitTime());

}

else if (attributeName.equals("RemoveWaitTime")) {

ret = new Long(getRemoveWaitTime());

}

else throw new AttributeNotFoundException(

"Queue.getAttribute(): ERROR: " +

"Cannot find " + attributeName + " attribute.");

}

return ret;



100



// . . .

}



The code itself is very simple: it's just a large if/else if/else construct in which we

attempt to match the attribute name that is passed as a parameter. If the attribute name

matches, we simply call the appropriate getter, wrap any fundamental types in their

appropriate JDK wrappers, and return the value to the caller. If the attribute value cannot

be found, we throw an AttributeNotFoundException with some information about the

requested attribute.

What if your management interface has no attributes? In that case, the body of this

method should throw an AttributeNotFoundException, because there are no attributes.

For example, if Queue has no attributes, this method will look like this:

public Object getAttribute(String attribute)

throws AttributeNotFoundException,

MBeanException,

ReflectionException {

throw AttributeNotFoundException("Attribute\'" + attribute +

"\' not found.");

}

3.2.2.4 setAttribute()



This method is used to set a single attribute value and is defined as:

public void setAttribute(Attribute attribute)

throws AttributeNotFoundException,

InvalidAttributeValueException,

MBeanException,

ReflectionException {

// . . .

}



What is passed to this class is an Attribute instance, a JMX class that wraps an

attribute's name and value as a pair. Attribute has two getters: one for the name and one

for the value. The name must match one of the attribute names that has been exposed on

the management interface of your MBean, or an AttributeNotFoundException will be

thrown.

public class Queue extends Basic implements DynamicMBean {

// . . .

public void setAttribute(Attribute attribute)

throws AttributeNotFoundException,

InvalidAttributeValueException,

MBeanException,

ReflectionException {

String name = attribute.getName();

Object value = attribute.getValue();

if (name.equals("QueueSize")) {

if (value instanceof Integer)



101



setQueueSize(((Integer)value).intValue());

else {

throw new InvalidAttributeValueException(

"QueueSize must be an Integer."

);

}



}

else throw new AttributeNotFoundException(

"Queue.setAttribute(): ERROR: " +

"Cannot find attribute \'" + name + "\'.");

}



}

// . . .

}



Queue has only one writable attribute, QueueSize, so that's the only one we have to



handle. But notice that we were careful to make sure the object passed inside the

Attribute instance is of the correct type—if it isn't, we throw an

InvalidAttributeValueException . This is standard practice when setting attribute



values inside setAttribute() and prevents all sorts of nasty runtime exceptions from the

JVM. Because it is possible that an ill-mannered management application may ignore the

information in the metadata and pass an attribute value of the wrong type, we should

always be careful and code for this contingency.

3.2.2.5 getAttributes()



This method is used to retrieve the values of a group of attributes and is defined as:

public AttributeList getAttributes(String[] attributeNames) {

// . . .

}



The attributeNames parameter is a String array that contains the names of the attributes

whose values are to be retrieved. It is very straightforward to implement this method by

delegating to the getAttribute() method for each attribute in the array:

public class Queue extends Basic implements DynamicMBean {

// . . .

public AttributeList getAttributes(String[] attributeNames) {

AttributeList resultList = new AttributeList();

for (int aa = 0; aa < attributeNames.length; aa++){

try {

Object value = getAttribute(attributeNames[aa]);

resultList.add(new Attribute(attributeNames[aa], value));

} catch (Exception e) {

// handle the exception. . .

}

}

return(resultList);



}

// . . .

}



102



After delegating to getAttribute(), a new Attribute object is instantiated and initialized

with the name of the attribute and its value. This is the AttributeList object that will be

returned to the caller.

3.2.2.6 setAttributes()



This method is used to set the values of a group of attributes and is defined as:

public AttributeList setAttributes(AttributeList attributes) {

// . . .

}



The attributes parameter is an AttributeList object. As we discussed earlier, the

AttributeList object contains a List of Attribute objects, each of which corresponds

to an attribute to be set. The following example shows how to access each Attribute

object from the AttributeList and set its value by delegating to setAttribute():

public class Queue extends Basic implements DynamicMBean {

// . . .

public AttributeList setAttributes(AttributeList attributes) {

AttributeList attributeList = new AttributeList();

for (int aa = 0; aa < attributes.size(); aa++) {

try {

Attribute attribute = (Attribute)attributes.get(aa);

setAttribute(attribute);

String name = attribute.getName();

Object value = getAttribute(name);

attributeList.add(new Attribute(name, value);

} catch (Exception e) {

e.printStackTrace();

}

}

return attributeList;

}

// . . .

}



Notice the emphasized lines. We retrieve the Attribute object from the AttributeList

and delegate the actual setting of the value to setAttribute(). The JMX specification states

that the attributes whose values were successfully set should be returned in an

AttributeList. It is conceivable that this list could differ from the AttributeList that

was passed into setAttributes(), so we must handle any exceptions thrown from

setAttribute() gracefully and continue to process the remaining Attribute objects.

Because the attribute that has been set may not be readable, we delegate the retrieval of

its new value to getAttribute(). If the attribute is not readable, getAttribute() throws an

exception that prevents the returned AttributeList object from containing the

Attribute object representing that attribute. If the attribute is readable, getAttribute()

simply returns the value and everything proceeds.



103



3.2.2.7 invoke()



This method is used to invoke an operation on an MBean's management interface and is

defined as:

public Object invoke(String operationName,

Object params[],

String signature[])

throws MBeanException,

ReflectionException {

// . . .

}



operationName is the name of the management operation to invoke and must match

exactly one of the operations for which there is a corresponding MBeanOperationInfo

instance. params is an array of objects that contains the actual parameter values that must

be passed to the operation. signature is an array of Class objects that represents the

signature of the operation.

We first check to see if the operationName parameter matches one of the operations

exposed on the MBean's management interface. If no match is found, a

ReflectionException should be thrown. Otherwise, this method should check to make

sure the signature matches the expected signature of the operation. If it does not, a

ReflectionException should be thrown. Next, the params array should be checked to

be sure that the objects passed are actually of the correct type. If they are not, a

ReflectionException should be thrown. If they are of the correct type, the invocation

should be made and the parameters passed. The following example shows how to

implement this algorithm for invoke():

public class Queue extends Basic implements DynamicMBean {

// . . .

public Object invoke(String operationName,

Object params[],

String signature[])

throws MBeanException,

ReflectionException {

Object ret = null;

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

reset();

}

else if (operationName.equals("suspend")) {

suspend();

}

else if (operationName.equals("resume")) {

resume();

}

else {

throw new ReflectionException(

new NoSuchMethodException(operationName),

"Cannot find the operation \'" +

operationName + " in " + dClassName);

}



104



}

// . . .

}



The logic inside this method is relatively simple. We just look at the operationName

parameter, and if it matches one of the operations for which we created an

MBeanOperationInfo instance, we invoke the appropriate method. Otherwise, we throw

a NoSuchMethodException wrapped inside a ReflectionException.



3.3 Dynamic MBean Inheritance Patterns

In this section, we will look at how the introspection process performed by the MBean

server causes the inheritance patterns available to dynamic MBeans to differ from those

available to standard MBeans.

Suppose that our application has the inheritance graph shown in Figure 3-2.

Figure 3-2. UML diagram showing a possible inheritance scenario for the sample

application



In this scenario, each class (with the exception of Supplier and Consumer) explicitly

implements the DynamicMBean interface. Based on the information in the figure, what

would you expect the management interface of, say, Controller to be? If you said that it

would be the union of the management interface of Basic and Controller, you would

be mistaken. You may recall from the previous chapter that using inheritance pattern #4

would allow a child class to augment its management interface with that of its parent

class. However, no such inheritance patterns are available for dynamic MBeans.

What do you suppose the management interface of Supplier is? In fact, it may be a

better question to ask whether Supplier is an MBean at all. The answer is yes, Supplier

is indeed an MBean (in fact, a dynamic MBean), because it may inherit the dynamic

MBean interface from its parent class. How is this possible? When Supplier is

105



registered with the MBean server, the MBean server performs its introspection and looks

to see whether Supplier exposes a management interface. Because it does not, the

MBean server next looks to its parent class, Worker. The MBean server notices that

Worker does implement an MBean interface, declares Supplier to be an MBean, and

delegates all MBean functionality to Worker (where the management interface is actually

implemented).

Let's look at another example. Suppose that we have the inheritance scenario shown in

Figure 3-3, again using the classes from the application.

Figure 3-3. Another possible inheritance scenario, shown in UML notation



Notice that Worker does not implement DynamicMBean. What impact do you suppose that

has on the management interface exposed by Supplier and Consumer? If you said that

Supplier and Consumer are dynamic MBeans whose management interface looks

exactly like that of Basic, you are correct. Let's look again at the introspection that is

performed by the MBean server, using Supplier as an example. Supplier does not

implement an MBean interface, so the MBean server moves up the inheritance graph to

Worker and notices that it does not implement an MBean interface either. The MBean

server continues up the inheritance graph to Worker's parent class, Basic, and notices

that it implements the DynamicMBean interface, so it declares Supplier a dynamic

MBean and delegates all MBean functionality to Basic. The MBean server will continue

walking the inheritance graph during its introspection process either until an MBean

interface is found or until the top of the inheritance tree is reached without finding an

MBean interface (at which point a NotCompliantMBeanException is thrown).

A class cannot implement both DynamicMBean and a standard

MBean interface. Suppose that Queue had been declared as:

public class Queue extends Basic

implements QueueMBean, DynamicMBean {



106



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Example 3-3. Creating an instance of MBeanInfo

Tải bản đầy đủ ngay(0 tr)

×