I l @ ve RuBoard |
Instrumentation is the layer of the JMX architecture closest to the MBean resource. The purpose of instrumentation is to define the management interface for an MBean so that an operator looking at a management console can see what is happening with the resource (via the MBean's attributes), control it as necessary (through the MBean's operations), and be sent events (via the MBean's notifications) should a condition with the resource require attention. 7.2.1 Avoid OverinstrumentationAn MBean should have "just the right amount" of instrumentation. But what does that mean, exactly? Well, it depends on who will be looking at the information provided by the management interface of your MBean. Will it be a developer, or an operator at a management console? The answer is important. Because management is focused on the health of a resource, an MBean should contain the minimum amount of information necessary to ensure that the resource is functioning within normal parameters. It is up to you to define these parameters. Remember, there is overhead associated with management, so use it carefully . At a fundamental level, management consists of three aspects:
Let's put this in a context to which most of us can relate: an automobile. An automobile is a system that can be monitored and controlled, and can send events to its operator. Here are some examples of each of these aspects of management:
Here is a simple questionnaire that can help you decide what to include in the management interface of a management-focused MBean. 7.2.1.1 Monitoring (attributes)A resource's management attributes make up its state, which gives an operator cues regarding its health. When identifying management attributes, ask:
7.2.1.2 Controlling (operations)This really boils down to one question: what actions must be performed on the resource to ensure the health of the system? Here are some examples using the automobile scenario:
In this example, as the operator of your car, you must have these control points to keep the system (and you along with it) healthy. However, not all MBean resources need to have management operations. It is possible that resources in your application will require no control at all. Simply accessing their attribute values and receiving their notifications is sufficient for that resource type. For other resources, it will be necessary for an operator to exert control over the resource to keep the system healthy. 7.2.1.3 Events (notifications)An event results in an unsolicited notification to the operator that the event has occurred. To identify the events associated with a resource, look at the management attributes and the range of possible values the attribute can have. Ask:
An event signals some condition regarding the state of the resource; usually the management attribute currently has a value that requires attention from the operator. For example, the low fuel warning light in your car comes on whenever the fuel level drops below some predefined level set by your car's manufacturer. When the fuel level drops below the predefined value (the event), the light comes on (the notification) and is a cue for you to check the fuel gauge (the management attribute), indicating that you need to find a gas station. Once you have identified the possible events, ask a simple question: must this event be reported to the operator for the system to remain healthy? If the answer is yes, a notification type should be created for the event. 7.2.2 Implement MBeanRegistrationSometimes a JMX agent is an MBean and might need to use the services provided by the MBean server to interact with other MBeans that are registered in the same MBean server. To do so, the agent needs a reference to the MBean server. In addition, a mechanism is needed for MBeans to provide their own object names when the agent is generic or is provided by another vendor. Finally, MBeans that are using system resources such as connections need to release these resources when the MBeans are no longer needed by the application. The MBeanRegistration interface provides the ability to handle all these situations. It defines four methods : public interface MBeanRegistration { public ObjectName preRegister (MBeanServer server, ObjectName name) throws java.lang.Exception; public void postRegister (Boolean registrationDone); public void preDeregister( ) throws java.lang.Exception; public void postDeregister( ); } The MBean server invokes these methods on MBeans that implement this interface. You can probably guess, based on the names of the methods when they are called during the process of registering and unregistering your MBean. There are several advantages to implementing this interface:
7.2.3 Use Dynamic MBeansDynamic MBeans provide significant benefits over standard MBeans for MBean resources that are deployed in production systems. As opposed to standard MBeans, whose metadata is created and maintained by the JMX implementation, the MBean developer creates dynamic MBean metadata. Therein lies the key to the power and flexibility of dynamic MBeans. However, with this power and flexibility comes a clear trade-off: complexity. Dynamic MBeans are not trivial to implement. But don't worry, at the end of this section I will provide a few recommendations that will make life easier when implementing dynamic MBeans. The JMX specification defines an interface called DynamicMBean and three types of dynamic MBeans that implement it:
The significant benefits of dynamic MBeans over standard MBeans are:
Let's look at each benefit in more detail. 7.2.3.1 Each MBean feature can have a descriptionAn MBean feature is a constructor, a parameter, an attribute, an operation, or a notification. Metadata about standard MBean features is created by the JMX implementation; you are not given a way to specify a description. And while it is possible to create dynamic MBean feature metadata without providing descriptions, you should never do so . These descriptions can be critical for an operator to understand how to properly interpret what he is looking at in the management console (and to decide whether to page you at 2:00 A.M.!). Here are some categories you should keep in mind when describing MBean features:
7.2.3.2 You, as the MBean developer, have control over the creation of the metadata . . .It is entirely up to you to make sure that the management interface advertised by the getMBeanInfo( ) method of DynamicMBean maps correctly to the underlying implementation. Because you create the metadata, MBean features can have intuitive names displayed to the operator, regardless of the names of those features in the underlying implementation. Ideally, the name of the underlying feature is the same as it appears on the management interface. However, if you have a compelling reason to make the feature name of the underlying resource different from the name you want the operator to see as part of the MBean metadata (say, the source code for the underlying resource is out of your control), you can do so. In addition, it is unclear how standard MBeans that emit notifications have that information communicated to a management console. Because the specification provides no standard mechanism for the MBean server to determine what notifications an MBean emits (it can only check to see if the MBean class implements NotificationBroadcaster ), the JMX implementation cannot create metadata for notifications emitted by standard MBeans. 7.2.3.3 The management interface can change dynamicallyThe management interface of a standard MBean is static. Any changes to the management interface require a recompile/redeploy step. However, the management interface of a dynamic MBean can change at runtime. This can be a useful feature for keeping the management interface as slim as possible: only those features that are required based on the current state of the MBean are added to the management interface of the MBean. Let's look at an example. Suppose you have an MBean that represents a queue with a management operation that allows the operator to suspend activity in the queue with a method called suspend( ) . When this operation is invoked, no more items can be added or removed from the queue. It then makes sense that there should be a management operation called resume( ) that allows activity to resume. However, this operation is not necessary when the queue is not suspended . In the implementation of suspend( ) , you can write code to dynamically add the resume( ) method to the management interface, as shown in Example 7-1. Example 7-1. Queue MBean with suspend capabilityimport javax.management.*; public class Queue implements javax.management.DynamicMBean { // . . . private MBeanInfo _mbi; public MBeanInfo getMBeanInfo( ) { return _mbi; } // . . . public synchronized void suspend( ) { _isSuspended = true; // Set member variable. MBeanInfo mbi = this.getMBeanInfo( ); MBeanOperationInfo[ ] ops = mbi.getOperations( ); MBeanOperationInfo[ ] newOps = new MBeanOperationInfo[ops.length]; for (int aa = 0; aa < newOps.length; aa++) { MBeanOperationInfo op = ops[aa]; // Replace "suspend" with "resume". if (op.getName( ).equals("suspend")) { newOps[aa] = new MBeanOperationInfo( " resume", // Name " Resumes activity in the queue.", // Description new MBeanParameterInfo[0], // Operation signature Void.TYPE.getName( ), // Return type MBeanOperationInfo.ACTION // Impact ); } else { newOps[aa] = ops[aa]; } } // MBeanInfo is immutable, so create new one. MBeanInfo newMbi = new MBeanInfo( mbi.getClassName( ), mbi.getDescription( ), mbi.getAttributes( ), mbi.getConstructors( ), newOps , mbi.getNotifications( ) ); _mbi = newMbi; notifyAll( ); } // . . . } Essentially, all you need to do is search the MBeanOperationInfo array for the suspend( ) method's metadata. Once you find it, simply create a new MBeanOperationInfo metadata object for the resume( ) method and place it in the array at the same location. All other operation metadata is used as is. Because MBeanInfo is immutable, you must create a new instance of it. You also replace resume( ) with suspend( ) when resume( ) is invoked and switch out the operation metadata in a similar fashion. Example 7-2 shows this. Example 7-2. MBean with suspend( ) replacing resume ( )import javax.management.*; public class Queue implements javax.management.DynamicMBean { // . . . private MBeanInfo _mbi; public MBeanInfo getMBeanInfo( ) { return _mbi; } // . . . public synchronized void resume( ) { _isSuspended = false; // Set member variable. MBeanInfo mbi = this.getMBeanInfo( ); MBeanOperationInfo[ ] ops = mbi.getOperations( ); MBeanOperationInfo[ ] newOps = new MBeanOperationInfo[ops.length]; for (int aa = 0; aa < newOps.length; aa++) { MBeanOperationInfo op = ops[aa]; // Replace "resume" with "suspend". if (op.getName( ).equals("resume")) { newOps[aa] = new MBeanOperationInfo( "suspend", // Name "Suspends activity in the queue.", // Description new MBeanParameterInfo[0], // Operation signature Void.TYPE.getName( ), // Return type MBeanOperationInfo.ACTION // Impact ); } else { newOps[aa] = ops[aa]; } } // MBeanInfo is immutable, so create new one. MBeanInfo newMbi = new MBeanInfo( mbi.getClassName( ), mbi.getDescription( ), mbi.getAttributes( ), mbi.getConstructors( ), newOps, mbi.getNotifications( ) ); _mbi = newMbi; notifyAll( ); } // . . . } When the queue is not suspended, the only action the operator can take is to suspend the queue because this is the only operation available. By the same token, when the queue is suspended, the only allowable action is to resume processing. This behavior is similar to how a graphical user interface application might add or remove items from a menu based on the state of the application. A well-designed MBean should have the ability to alter its management interface to match those attributes, operations, and notifications that are appropriate to its current state. 7.2.3.4 There are fewer source files to maintainEvery time the management interface of an MBean changes, you must change two source files: the MBean interface and the class that implements it. This means that by using dynamic MBeans, you cut in half the number of MBean-related source files that have to be maintained! 7.2.4 Use a Dynamic MBean Fa §ade PatternMuch of the code for a dynamic MBean is similar from one dynamic MBean to another. Hence, there are two primary tasks on which to focus:
Dynamic MBean metadata classes are immutable, so the only way to set their various properties is to use a constructor. In addition, a metadata class must be created ”and code written ”for every feature on the management interface of a resource, resulting in lots of code that basically looks the same. A simplified approach to creating dynamic MBean metadata would help to reduce code clutter (and hence, readability). Here is an example of how to create a dynamic MBean metadata class for an attribute called MyAttribute of type Integer that is read-only: MBeanAttributeInfo attribute = new MBeanAttributeInfo( "MyAttribute", // Name Integer.class.getName( ), // Type "My very own attribute.", // Description true, // Is Readable? false, // Is Writeable? false); // isIs (i.e., is boolean?) Of course, you could have squeezed all the parameters onto one line of code, but that would sacrifice readability (and could introduce errors). For example, JMX will allow you to create incorrect metadata for the attribute we just looked at: MBeanAttributeInfo attribute = new MBeanAttributeInfo( "MyAttribute", // Name Long.class.getName( ), // Type "My very own attribute.", // Description true, // Is Readable? false, // Is Writeable? false); // isIs (i.e., is boolean?) In this example, this attribute is incorrectly described as a java.lang.Long instead of its correct type, java.lang.Integer . This mistake will not be caught until runtime, and can lead to hours of needless debugging to fix. The reverse situation could also occur if the data type of the attribute changes (as a result of, say, maintenance to the resource class), but you forget to modify the metadata to match the attribute's new type. Again, this could lead to wasted hours of debugging. Much of the code for the second task, to implement the DynamicMBean interface, is similar from implementation to implementation. For example, the getAttribute( ) method is passed the name of an attribute and retrieves its value. Suppose MBean A has an attribute A, and MBean B has an attribute B. MBean A's implementation of getAttribute( ) might look like this: public class MBeanA implements DynamicMBean { // . . . public Object getAttribute(String name) throws // Some exceptions { try { if (name.equals("A") { return this.getA( ); } } catch (Exception e) { // Handle . . . } } } while MBean B's implementation might look like this: public class MBeanB implements DynamicMBean { // . . . public Object getAttribute(String name) throws // Some exceptions { try { if (name.equals("B") { return this.getB( ); } } catch (Exception e) { // Handle . . . } } } In this extremely simple example, only the highlighted lines of code are different, and all they do is figure out if the requested attribute is supported and delegate to the appropriate getter. Suppose you introduce a class called DynamicMBeanFacade , which looks similar to the class shown in Example 7-3. Example 7-3. DynamicMBeanFacadepublic class DynamicMBeanFacade implements DynamicMBean { public DynamicMBeanFacade(Object managedResource) { // . . . } public void addAttribute(String name, String description) { // . . . } public void removeAttribute(String name) { // . . . } public void addOperation(String name, String description) { // . . . } public void removeOperation(String name) { // . . . } public void addNotification(String type, String description) { // . . . } public void removeNotification(String type) { // . . . } // DynamicMBean interface implementation here . . . } There is a method that adds and removes each feature to be defined on the management interface of the resource. For example, to add the MyAttribute attribute that we saw earlier, the agent code looks like this: public void someAgentMethod( ) { try { MBeanServer mbs = /* Obtain somehow. */ DynamicMBeanFacade resource = (DynamicMBeanFacade) mbs.instantiate("MyClass"); resource.addAttribute("MyAttribute", "My very own attribute"); // . . . } catch (Exception e) { // Handle . . . } } This pattern describes the use of a fa §ade over these two tasks and provides three key benefits:
Here's how it works. The agent creates an instance of DynamicMBeanFacade and invokes the appropriate methods to describe the attributes, operations, and notifications (i.e., the features) that will be part of the management interface of the managed resource. The agent is only required to pass the name and description of the feature. DynamicMBeanFacade uses Java's reflection API to introspect the managed resource and discover data type information for attributes. For operations, DynamicMBeanFacade determines the return type and any parameter names and their types. Only those attributes and operations that actually exist on the underlying resource can be added to the management interface, which presupposes some knowledge of the underlying resource on the part of the agent. In the following sections, we will take a quick look at some of the methods of DynamicMBeanFacade and how they can be implemented for each MBean feature type, which should give you an idea of how an implementation of DynamicMBeanFacade might look. For a complete source listing of the DynamicMBeanFacade , refer to http://www.oreilly.com/catalog/javaebp for information on how to download the sample application code for this chapter. You can employ two strategies when using this pattern:
For the sake of brevity, the examples in this section will use the first strategy because it is slightly easier to implement. However, the implementations are remarkably similar, and I will briefly describe the differences at the end of this section. To implement the addAttribute( ) and addOperation( ) methods of DynamicMBeanFacade , follow these steps:
7.2.4.1 AttributesFor each attribute in the managed resource, the agent calls addAttribute( ) , passing the name and description of the attribute. Based on the JMX design patterns, DynamicMBeanFacade is expecting the name of the attribute to be preceded by get or is for readable attributes and/or set for writeable attributes. If the attribute is called MyAttribute , the implementation of addAttribute( ) will introspect the managed resource for getMyAttribute( ) and/or setMyAttribute( ) ( isMyAttribute( ) is allowed as a getter for boolean attribute types). As you saw earlier, describing the management interface is very simple. Of course, the trade-off is that the bulk of the work falls to the implementation of DynamicMBeanFacade . Even still, it's not terribly complicated. Let's look at a very simple implementation of the addAttribute( ) method, as shown in Example 7-4. Example 7-4. Implementation of the addAttribute( ) methodpublic class DynamicMBeanFacade implements DynamicMBean { // . . . public synchronized void addAttribute(String name, String description) throws DynamicMBeanFacadeException { Class current = this.getClass( ); boolean isReadable = false; boolean isWriteable = false; boolean isIs = false; String type = null; Method[ ] methods = current.getMethods( ); for (int aa = 0; aa < methods.length; aa++) { String methodName = methods[aa].getName( ); if (methodName.equals("get" + name)) { isReadable = true; type = methods[aa].getReturnType( ).getName( ); } else if (methodName.equals("set" + name)) { Class[ ] parmTypes = methods[aa].getParameterTypes( ); if (parmTypes.length > 1) { continue; } else { isWriteable = true; type = parmTypes[0].getName( ); } } else if (methodName.equals("is" + name)) { type = methods[aa].getReturnType( ).getName( ); if (type.equals(Boolean.TYPE.getName( ))) { isReadable = true; isIs = true; } else type = null; } } if (!isReadable && !isWriteable) throw new DynamicMBeanFacadeException("Attribute not found."); MBeanAttributeInfo attribute = new MBeanAttributeInfo( name, type, description, isReadable, isWriteable, isIs); // Recreate MBeanInfo using new attribute metadata. } } This method searches all the public methods of the managed resource class for a getter and/or setter for the specified attribute. If the attribute is found, an MBeanAttributeInfo metadata object for the attribute is created and added to the existing attribute metadata by recreating the MBeanAttributeInfo array. The MBeanInfo object that describes the management interface is recreated (because MBeanInfo instances are immutable) using this new attribute metadata array. When an agent wishes to remove an attribute from the management interface, it invokes the removeAttribute( ) method, passing the name of the attribute to remove. In this case, DynamicMBeanFacade does not need to introspect the managed resource class; it only needs to search the metadata that describes the attributes of the management interface and remove the metadata describing the specified attribute. 7.2.4.2 OperationsWhen the agent wishes to add an operation to the management interface, it invokes the addOperation( ) method, passing the name and description of the operation. The implementation of addOperation( ) must then introspect the managed resource to ensure that the operation actually exists and create the necessary metadata to describe the operation, including metadata for any parameters to the operation. The metadata is then added to the MBeanOperationInfo array that is part of the MBeanInfo object that describes the management interface, as shown in Example 7-5. Example 7-5. DynamicMBeanFacade using metadatapublic class DynamicMBeanFacade implements DynamicMBean { // . . . public synchronized void addOperation(String name, String description) throws DynamicMBeanFacadeException { Class current = this.getClass( ); StringreturnType = null; Method[ ] methods = current.getMethods( ); Method theMethod = null; for (int aa = 0; aa < methods.length; aa++) { if (methods[aa].getName( ).equals(name)) { theMethod = methods[aa]; break; // Take only the first one encountered. } } if (theMethod = = null) throw new DynamicMBeanFacadeException("Operation not found."); MBeanOperationInfo newOp = new MBeanOperationInfo(description, theMethod); // Recreate MBeanInfo using new operation metadata. } // . . . } The algorithm used to create the operation metadata, adding it to the operation metadata array, and recreating the MBeanInfo object should look familiar: this process is almost identical to addAttribute( ) . In Example 7-5, I have used an alternate constructor of MBeanOperationInfo that takes a java.reflect.Method object. This constructor will perform further introspection and create the necessary metadata to describe the operation's parameters. When an agent wishes to remove an operation from the management interface, it invokes the removeOperation( ) method, passing the name of the operation to remove. In this case, DynamicMBeanFacade does not need to introspect the managed resource class; it only needs to search the metadata that describes the operations of the management interface and remove the metadata describing the specified operation. 7.2.4.3 NotificationsWhen the agent wishes to add a notification emitted by a managed resource to the resource's management interface, it invokes the addNotification( ) method, passing the type (i.e., name) of the notification and a description of the notification. Unlike attributes and operations, there is really no way for DynamicMBeanFacade to introspect the managed resource class to see if the notifications exist because it is entirely up to the managed resource to define and emit JMX notifications and communicate this information to the agent developer a priori through, say, documentation. Assume that the managed resource implements the NotificationBroadcaster interface, as shown in Example 7-6. Example 7-6. Implementing the NotificationBroadcaster interfacepublic class DynamicMBeanFacade implements DynamicMBean { // . . . public synchronized void addNotification(String type , String description) throws DynamicMBeanFacadeException { MBeanNotificationInfo notif = new MBeanNotificationInfo( new String[ ] {type} , " javax.management.Notification" , description); MBeanNotificationInfo[ ] notifs = _mbi.getNotifications( ); MBeanNotificationInfo[ ] newNotifs = new MBeanNotificationInfo[notifs.length + 1]; System.arraycopy(notifs, 0, newNotifs, 0, notifs.length); newNotifs[notifs.length] = notif; // Recreate MBeanInfo with new notification metadata array. } // . . . } When an agent wishes to remove a notification from the management interface, it invokes removeNotification( ) , passing the notification type. 7.2.4.4 DynamicMBean implementationImplementing the DynamicMBean interface is actually very straightforward, as you'll see. Example 7-7 shows how to implement invoke( ) , which you can use as the implementation backbone of all the other methods of DynamicMBean . Example 7-7. Implementing the DynamicMBean invoke( ) methodpublic class DynamicMBeanFacade implements DynamicMBean { // . . . public Object invoke(String actionName, Object[ ] params, String[ ] signature) throws javax.management.MBeanException, javax.management.ReflectionException { Object ret = null; // Return code try { Class[ ] sig = new Class[signature.length]; for (int bb = 0; bb < signature.length; bb++) sig[bb] = this.getClassFromClassName(signature[bb]); Class mr = this.getClass( ); Method method = mr.getMethod(actionName, sig); ret = method.invoke(_managedResource, params); } catch (Exception e) { throw new ReflectionException(e); } return ret; } // . . . } This is not the most robust implementation possible, but even this simple implementation does a lot in remarkably few lines of code. This method creates the array of Class objects that represents the method signature, uses this signature to obtain a Method object from the reflection API, and invokes the method. Any exceptions that arise will be caught and rethrown in the form of a javax.management.ReflectionException . 7.2.5 Externalize the Description of an MBean's Management InterfaceThe purpose of a management interface is to define the contract between an operator and the resources in your application. JMX defines patterns and an API for defining the management interfaces for your MBeans. A standard MBean's management interface is static because it is defined using a Java interface. As a result, you are required to code the instrumentation necessary to support the definition of the management interface according to JMX MBean design patterns. A dynamic MBean's management interface is described programmatically through the use of metadata classes. The necessary instrumentation to support the definition of the management interface is a combination of your implementation of DynamicMBean and the methods in the underlying MBean resource. Regardless of the type of MBean strategy you choose, the definition of the management interface is internalized, or internal to the MBean resource code. The main issue with instrumenting and defining a resource's management interface entirely in source code is that only a Java programmer can change it. Should the management interface need to be altered at deployment time, for example, the deployer needs to have Java programming skills, or access to a Java programmer. This is not a new concern and is one that has been addressed in a number of ways. The Enterprise JavaBeans (EJB) specification provides a mechanism to handle deployment-time modifications to the runtime behavior of Enterprise beans through the env-entry element of the ejb-jar file deployment descriptor. The bean provider offers an enterprise bean with more flexibility by externalizing certain control points specific to the deployment environment. Externalizing the definition of the management interface of an MBean has similar benefits, allowing you to move the definition of an MBean's management interface outside your code base. You still have to write code for operations, send notifications, and sense and maintain attribute values. But the description of the management interface will be maintained outside the code base. You cannot use this best practice if your MBeans are standard MBeans because a standard MBean's management interface definition is in a Java interface. [2]
The benefits of this best practice are:
There are many ways to maintain the external representation of an MBean interface such as a text file, relational database, or LDAP. Regardless of the medium, the language used to express the definition will be XML. In the example implementation in this section we will use an XML document that resides on the local filesystem. The XML grammar used will be sufficient to describe management attributes, operations, and notifications. The XML document will be validated using a DTD. In a generic sense, the agent must:
In the implementation we will look at, the agent instantiates the managed resource, reads the XML definition for the management interface definition that corresponds to the managed resource class, and creates a DynamicMBeanFacade (which we looked at earlier in this chapter) to use as the MBean for the managed resource. The DTD that defines the structure of valid XML documents for this example is defined as: <!ELEMENT application (mbean+)> <!ELEMENT mbean (description?, (attribute operation notification)+)> <!ATTLIST mbean className CDATA #REQUIRED> <!ELEMENT description (#PCDATA)> <!ELEMENT attribute (#PCDATA)> <!ATTLIST attribute name CDATA #REQUIRED> <!ELEMENT operation (#PCDATA)> <!ATTLIST operation name CDATA #REQUIRED> <!ELEMENT notification (#PCDATA)> <!ATTLIST notification type CDATA #REQUIRED> That's about as simple as it gets. This DTD defines XML documents that describe one or more MBeans. For each MBean, there is an optional description for the MBean, and there must be at least one attribute, operation, or notification. Attributes and operations must have an attribute called name , and notifications must have an attribute called type . The description of an attribute, operation, or notification is provided optionally as the value of that element. Example 7-8 shows an MBean called Queue that has the following features:
Information such as this is communicated to the deployer, who does not necessarily have access to the source code. Let's suppose that all the deployer knows are the available attributes, operations, and notifications that can be defined on the management interface through the use of an XML file whose structure is dictated by the DTD we looked at earlier. Example 7-8 shows how such an XML definition of the management interface for the Queue class might look. Example 7-8. XML definition of the management interface for Queue<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE application SYSTEM "mbean.dtd"> <!-- Root element: application --> <application> <!--= = = = = = = = = = = = = = = = = = = = = = = =--> <!-- Queue MBean definition --> <!--= = = = = = = = = = = = = = = = = = = = = = = =--> <mbean className="jmxbp.Queue"> <description>This MBean is a managed queue to which one or more supplier threads is adding items (objects) and one or more consumer threads is removing them.</description> <!-- ********** --> <!-- Attributes --> <!-- ********** --> <attribute name="NumberOfItemsProcessed"> The number of items that have been removed from the queue.</attribute> <attribute name="AddWaitTime"> The total time (in milliseconds) all supplier threads have waited to add items to the queue because it was full.</attribute> <attribute name="Suspended">Will be true if the queue is currently suspended.</attribute> <attribute name="QueueFull">Will be true if the queue is currently full.</attribute> <attribute name="QueueEmpty">Will be true if the queue is currently empty.</attribute> <attribute name="RemoveWaitTime"> The total amount of time (in milliseconds) that all consumer threads have waited to remove items from the queue because it was empty. </attribute> <attribute name="QueueSize"> The maximum number of items that can coexist in the queue at any one instant. Also referred to as the queue depth.</attribute> <!-- ********** --> <!-- Operations --> <!-- ********** --> <operation name="suspend"> Suspends activity in the queue. All threads are blocked until the resume operation is invoked.</operation> <operation name="resume"> Resumes activity in the queue. All previously blocked threads are notified that they might become eligible for scheduling.</operation> <operation name="enableTracing">Enables logging of trace-level information to provide more detailed runtime diagnostic information.</operation> <operation name="disableTracing">Disables logging of trace-level information.</operation> <!-- ************* --> <!-- Notifications --> <!-- ************* --> <notification type="sample.Queue.queueStalled"> This notification is sent when logic internal to the queue has determined that the queue is stalled in some way. This generally means that no processing has occurred for more than 10000 ms (10 seconds) and that other attributes of this class need to be investigated to resolve the problem.</notification> </mbean> </application> In this simple example, only the jmxbp.Queue class is defined as an MBean. When the application is started, the agent reads in the XML document describing the management interface for jmxbp.Queue and uses the DynamicMBeanFacade from earlier in the chapter. In this example, JAXP is used to process the XML file and is included in the example download for this chapter. This is a working example with some of the details elided for simplicity. The following code comments show the major steps involved: try { DynamicMBeanFacade mbean = null; String className = "jmxbp.Queue"; Queue queue = (Queue)mbs.instantiate(className); // Set up the XML parser. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance( ); dbf.setValidating(true); DocumentBuilder db = dbf.newDocumentBuilder( ); Document config = db.parse(new File("c:homemyapp.xml")); // Look through document for element nodes called "mbean". NodeList nodes = n.getChildNodes( ); for (int aa = 0; aa < nodes.getLength( ); aa++) { Node node = nodes.item(aa); if (node.getNodeType( ) = = Node.ELEMENT_NODE && node.getNodeName( ).equals("mbean")) { NamedNodeMap atts = node.getAttributes( ); // If "className" attribute equals className, read all MBean attributes, // operations, and notifications. Node att = atts.getNamedItem("className"); if (att.getNodeValue( ).equals(className)) { String mbeanDesc = this.getElementText(node); mbean = new DynamicMBeanFacade(resource, mbeanDesc); NodeList features = node.getChildNodes( ); for (int cc = 0; cc < features.getLength( ); cc++) { Node feature = features.item(cc); if (feature.getNodeType( ) = = Node.ELEMENT_NODE) { NamedNodeMap atts2 = feature.getAttributes( ); // Add attribute, operation, or notification to the management interface, // providing its name and description from the XML document. if (feature.getNodeName( ).equals("attribute")) { Node nameNode = atts2.getNamedItem("name"); String attName = nameNode.getNodeValue( ); String attDesc = this.getElementText(feature); mbean.addAttribute(attName, attDesc); } else if (feature.getNodeName( ).equals("operation")) { Node nameNode = atts2.getNamedItem("name"); String opName = nameNode.getNodeValue( ); String opDesc = this.getElementText(feature); mbean.addOperation(opName, opDesc); } else if (feature.getNodeName( ).equals("notification")) { Node nameNode = atts2.getNamedItem("type"); String notifName = nameNode.getNodeValue( ); String notifDesc = this.getElementText(feature); mbean.addNotification(notifName, notifDesc); } } } // for (int cc . . . } // att.getNodeValue( ).equals(className) } if (mbean != null) break; } } catch (Exception e) { // Handle . . . } Keep in mind that this is only one possible way to implement externalization of the MBean interface. It is not necessary to define a DTD (or XML schema) to validate the XML definition, but it makes the agent code simpler to write because the XML parser can be used to enforce constraints, such as the presence of certain attributes, for example. 7.2.6 Use createMBean( ) to Create MBeansOne of the purposes of the MBean server is to act as a communications bus for interacting with MBeans. You should never manipulate an MBean through a direct object reference, for several reasons:
|
I l @ ve RuBoard |