Design Patterns, JMX for Manageability
Controls and visibility are essential for monitoring and managing applications. Learn a best practice for employing design patterns that combine applications with JMX and policies.
- By Justin Murray
The Java Management Extensions (JMX) technology is a key part of both the Java 2 Platform's Standard Edition (J2SE) 5 and Enterprise Edition (J2EE). Support for this standard is now required in J2EE-compliant application servers, some of which use JMX MBeans as the base infrastructure. Design patterns have been popular since they were introduced, and we can apply design ideas to the manageability part of an application.
We'll describe a set of design patterns for making applications easier to manage in production by combining them with the use of JMX and logging, while making policies external to the code. This part of the application is important to the architect because most of the application's life cycle will be in its deployment phase. Controls and visibility are necessary for operations people to monitor and manage the application well. Monitoring and managing applications are best done today by instrumenting the application with JMX for certain dynamic requirements continuing to use the more traditional logging for other purposes.
You can build your own custom MBeans to be used alongside the application server-supplied or Java Virtual Machine (JVM)-supplied MBeans. This assembly is best done when the manageability requirements of the application's internal workings demand higher visibility to the application's internal objects and attributes. Logging messages to files or other destinations from the application is a common technique that gives the operations personnel some degree of monitoring the running product. Logging messages is an effective method for tracking exceptions or static error conditions (from informational to fatal) that occur within the application. Logging is essential for letting operations know what is happening inside the application.
However, for rapidly changing application performance datathat is, data for measured quantities in the overall performance of the applicationlog files do not make a good fit. It is not a good idea, for example, to put a new log message out as a counter for each user that logs into a system, when that number can change each second or more frequently.
It makes better sense to collect that rapidly changing data in an objecta JMX MBeanand gather values periodically from that MBean into a management tool. This technique can be used to capture the number of transactions in progress in the system, for example, or the length of a queue that contains requests waiting to be processed. Comparing the number of business transactions being processed with the number waiting to be processed in the queues may give us a good picture of how well the application is behaving.
Using MBeans in this manner is one of the best practices for using JMX. MBeans are good objects for counting quantities that need to be made visible to the management of the application. These items may be graphed over time when they are eventually collected from the MBean to show relative performance, or to show one of the application's other business metrics. JMX MBeans may be used for quantities that are unchanging, such as a warning, exception, or error message, but that is not their best use.
Consider this description for code that needs to be written to conform to the JMX model. The question to consider here is: what aspects of using this model may change most frequently?
- MBean objects are created to provide data sources and control points for the application's manageability. A business object may itself implement or inherit the appropriate MBean interfaces to become an MBean in its own right, which involves creating the business object so that it adheres to the rules for the Standard MBean, or DynamicMBean, interfaces.
- The MBean objects are made accessible for use through registration with the MBean server, a component of the application server, or the JVM process.
- The name used for the MBean at registration time is a unique string, which must be used by any other objects acting in the role of consumers of the MBean (for it to be correctly found).
- An object calls a method on an MBean by supplying the MBean name, the name of a method on it (both supplied as Strings) to the MBean Server, and asks the latter to invoke that method on the MBean.
- The MBean may emit notifications at certain times to identify the occurrence of certain conditions.
Now that we have an overview of using JMX for manageability, let's examine specific uses of patterns. Clearly, having string-based names for objects and for methods scattered throughout the application code is a liability. These names are subject to changes and errors; therefore, we should encapsulate them in some form of pattern, which is the motivation for the MBeanHelper pattern.
The MBeanHelper pattern removes some of the details of MBean access from an object that seeks to use an MBean (see Figure 1). This pattern solves the problem of having a tight linkage between a business object and an associated MBean. This linkage is based on the need for a string for the name of the object and another for a method call and its parameters. These strings defeat the strong typing that we would like to maintain between objects. The name of the MBean to be used is encapsulated in the Helper class. The name of the method to be invoked is also used through a strongly typed interface to the MBeanHelper class, thus providing compile time type checking. The Helper class hides from the consumer all the code for looking up the MBean in the MBean server.
One important way in which we build manageability into an application is by instrumenting one or more of its business objects such that they write important data into the JMX MBeans. We do not, however, want to scatter too much JMX-specific code throughout the logic of these business objects. We want the instrumentation to be clean in the sense that a simple method call to a JMX MBean is all that is required to be done in the business code, which means hiding all the machinery of naming and finding the appropriate MBean from the business object. This instrumentation should all be done under the covers of a design pattern.
The MBeanHelper pattern is designed for this purpose. It looks up the correct MBean, using the MBean name, in the MBean server's repository and invokes the right method. The MBeanHelper is not itself an MBean, just a well-known end point for a certain type of MBean, that can be used as a gateway to them.
Strings provide the application developer no protection from having the name of the MBean or the name of method entered incorrectly. The compiler will never discover this mistake because it knows nothing about the name of the MBean or the rules that apply to its name. An invocation of a method on an MBeanHelper class, however, is type checked by the compiler. We need only to make the String-based MBean names known to this Helper class and test it for accuracy, rather than testing every usage of the MBean in the application. Additions to the JMX libraries at version 1.2 have provided InvocationHandlers for a similar purpose.
The MBeanHelper object will be required to exist for the same duration as the MBean itself. Handling the lifetime of the MBeanHelper must now be considered as an extra task. There is also a trade-off between the cost of a method call on the MBeanHelper and the cost of the MBean method invocation itself, which may affect performance.
The MBeanHelper is the sole interface to the MBean, so if we want to implement notifications coming out of the MBean, we must do so also for the MBeanHelper.
Now let's look at the MBeanForAppConfig pattern, which encapsulates one of the simplest uses of an MBeana container for application startup and run-time configuration information (see Figure 2).
An MBean for application configuration data lets the application gather the initial configuration from some external source at startup (for example, a property file). Then, with suitable design of the application, operators are allowed to change elements of the application configuration. These changes may be made through notifications from the configuration MBean to other objects during the application's life cycle without a restart. We may wish to change the level of logging we see, for example, to get more data about what is going on inside the application.
One or more MBeanForAppConfig objects may conform to this style in the application, each controlling a different part of the application's configuration, such as user configuration, database connections configuration, or others, depending on the complexity of the configuration requirements.
At application startup time, the MBeanForAppConfig object reads the configuration properties from a file, database, or other media in which the administrator has set up the various properties. The MBeanForAppConfig object then allows authorized administrators to access and change the values of items that are configurable through a management tool, perhaps a browser-based one. The changes that occur are propagated to the relevant business objects through the Observer pattern or a JMX notification, if that is required.
The MBeanForAppConfig object may consult a separate object to determine whether the property being set conforms to an allowed value, disallowing the new setting if it does not conform. We can create yet another MBean (ideally a dynamic MBean) that captures the allowed values of the data items that make up the application configuration, but this MBean is probably too much for most applications. This logic may also be stored within the MBeanForAppConfig object itself.
The MBeanForAppConfig object can also be designed in such a way that it writes any configuration changes back to a file. It may write these changes back to the properties file from which it first derived its initial values, if that task was considered to be useful. This procedure would apply when any changes made to the configuration during one run time would need to be reflected into the next startup configuration.
The MBeanForAppConfig object must live for the lifetime of the process if changes are allowed to occur at any point in the application's lifetime. Using a notification scheme, which is available in JMX also, the object transmits its changes of state in a disconnected way. The list of NotificationListener objects that receive notifications from it may change over time, so the MBeanForAppConfig object should not concern itself with their lifetimes.
One approach to designing for application manageability allows any business object in the application to have an MBean style of interface by creating an MBean interface and then designing the business object to implement that interface. This technique has the advantage of being fairly straightforward to implement. It means that the business objects provide both business functionality and management functionality in the same object.
However, this technique can lead to problems. It means that any tool that touches the management interface of the object directly interferes with the business object's performance, for example. When the business object is handling a critical transaction, this business object may also be requested to respond to an operator intervention through its JMX interface at the same time. This situation suggests that we would need to build a barrier against operator intervention when the business object is executing in a critical section of the business logic. We run the risk of locking the object for too long when we do this.
At the implementation or code level, the mixing of business with management concerns has other undesirable implications (as will be described in the discussion of the SeparateMBean pattern). In general, therefore, we would like to think about the management interface to our systemat design timeas separate from the business interfaces. These two interfaces are implemented by different objects.
The SeparateMBean pattern describes the creation of an MBean that is a separate object from the business object or objects that are required to be managed. The mixing of management concerns into an object along with business concerns spoils the cleanness of the business interface. Mixing these could have some security concerns, such as an operator invoking a business method, or a businessperson invoking an operational method, in error. In a mixed interface situation, the developer of the business logic must also understand all the implications of the management or JMX-related methods of the object. Design of the MBean interface and implementation are tied to the design of the business object; although in many teams separate architects might do these two types of design.
The SeparateMBean pattern solves these problems by separating the MBean to a different object from the business object to which it relates. As business objects are created, they do not implement the MBean interface. Instead, they maintain a reference to a separate MBean object that acts as their management party. The creation time of the managing MBean may be the same as, or different from, that of the business object.
The BusinessObjectManager (see Figure 3) representing the SeparateMBean pattern is created either by the BusinessObject or by some other party so that it can manage one or more instances of the class to which the BusinessObject belongs. The BusinessObjectManager contains all the logic necessary to supply a management interface, which may be used by the BusinessObject or others. The BusinessObject calls methods or sends notifications to the BusinessObjectManager to update it with information that is relevant to management tools. The BusinessObjectManager could also call methods on the BusinessObject to retrieve this information.
Using this pattern provides separation of concerns through use of more objects. Where a business object also served as a management object in the past, this pattern has two objects with different purposes.
The price paid for using this pattern is that a reference is now maintained between the business object and the MBean that manages it. Using the inheritance/interface implementation method for the creation of a business object that is also an MBean allows us to dispense with that reference. Using this pattern also requires more intelligence in the creation Factory as to when to create the BusinessObjectManager MBean (perhaps once for the whole set of BusinessObject instances).
A design decision is required in the use of this pattern to determine whether the business object actively sends data through a method call to the BusinessObjectManager MBean, or whether the latter polls the business object for data at intervals. Both models may be useful for different circumstances, but to conserve performance, the push model should be used in most casesmeaning that the business object takes the initiative to push data out to the MBean object, when necessary.
Policy Separation Pattern
The purpose of the SeparatePolicyfromMBeanPattern pattern is to keep all considerations of changeable management policies out of the MBean that gathers performance data (see Figure 4). Our first approach is to use MBeans as data containers, not as algorithm implementations for manageability policies. At a minimum, we would want to separate these two entities into different objects.
The action of collecting the length or depth of a vital queue of items on a JMS topic, for example, is separate to the policy of issuing an alert whenever the queue stays at 50 entries for more than a minute. The latter concern is to do it with a management policy. The former is to do it with data gathering. We could store the logic for the policy side in an MBean itself, or we could store it outside the process completely in a separate management engine. We can both store data in an MBean's attributes and carry out calculations on that data. Only the most basic calculationsif any at allshould be done in the MBean code.
If we want to compromise this principle of extracting the management policy from the MBeans, we might consider having separate MBeans that hold the changeable policies and those are accessible from the outside for change. By separating the MBean as a container of data from the rules on how it is processed, we gain more flexibility over the ways in which these policies can change in time.
An important structuring pattern in application manageability design is to use MBeans as data containers only, not as algorithm holders for manageability policies. These policiesfor example, "When the number of shopping carts used at one time in my application goes above 1000, print a message on the management console"are subject to change. Capturing these policies is the job of a tool outside the MBean domain; therefore, that change can be made without tampering with the MBean logic and having to recompile it. However, it will be done for specific reasons. Design is always a compromise, so these best practices and patterns aren't intended to be seen as hard and fast rules that fit all situations.
We've discussed a set of design patterns for enabling the construction of manageability aspects of a Java, J2EE, or Web services application. Good manageability is essential for success of an application once it moves into production. Often in production situations nondevelopers manage applications. Each pattern has benefits and costs for using it. Care should be taken to profile the performance of the application while building manageability into it and using these patterns at implementation time.
The application designer should consider the application's manageability needs during the design phase, not later. One choice is whether to separate the business-related logic from the manageability logic and to provide separate objects in the application for these concerns. This type of separation of concerns is recommended and is shown in the MBeanHelper pattern.
This is a starting point for a larger collection of design patterns for JMX and application manageability, and I hope interested architects and software designers will contribute more patterns to the collection. The HP developer Web site provides a forum for the developer community to contribute and discuss design patterns for manageability.
Download the related code for this article here.