Standard MBeans

When the MBean server performs its introspection, it sees the getQueueSize() method, indicating a readable attribute called QueueSize. It also notices a setQueueSize() method that takes the correct number of parameters for a setter but also provides a return value. Because a setter cannot return a value, the MBean server interprets the method as a management interface operation instead of as an attribute setter. Thus, in our example, a read-only attribute called QueueSize and an operation called setQueueSize() that takes an int parameter and returns an int would be exposed. (Note that the choice of int as the return value in this example was purely arbitrary. This mistake would happen if we had used any other type as well.)

If the management interface for your MBean does not show up as you expected, check the return values of all of the setter methods on your MBean interface. Any setter methods that return a value of any type other than void will be exposed as operations called setSomething(), where Something is the name of the attribute. Remember, a proper setter must return void!

What if the return value type of the getter is different from the parameter type of the setter? This is probably the least common mistake, as it is usually the result of mistyping the declaration of either the getter or the setter. Because the declaration must be typed twice (unless you copy the declaration from the interface and paste it into the implementing class), this mistake is not likely to occur often. However, because it has baffling results, I wanted to mention it here. Suppose you mistakenly define the management interface as:

Do you see the problem? Notice that the parameter type to the setter is a long. This interface definition for the QueueSize attribute certainly follows the rules: the getter takes no parameters and returns a value, while the setter returns void and takes only one parameter.

So what do you suppose is exposed as the management interface? In the JMX 1.0 RI, the management interface exposed is a write-only attribute of type long. That's it—there is no getter. Because of the conflicting types, the MBean server has to choose what is exposed, and the setter wins. Exactly what is exposed in the JMX you use depends on how the MBean server's introspection is implemented. However, the point is the same: make sure the parameter type of the setter and the return type of the getter match!

Mistake #3: Wrong number of parameters

Suppose we define a getter that takes an argument on the management interface:

When the MBean server performs its introspection, it will detect that getQueueSize() takes a parameter and will expose it as a management operation instead of as the getter for the QueueSize attribute. A proper getter must take zero arguments.

A setter must take only one argument, and that argument must be of the type of the attribute it is to set. Suppose that the management interface is defined as:

When the MBean server performs its introspection, it exposes a read-only attribute called QueueSize and an operation called setQueueSize() that takes an int and a char parameter and returns void.

If the management interface for your MBean does not appear as you expected, check the number of arguments to all of the setter methods on the MBean interface. Remember, a proper setter must take only one parameter!

Implementing the MBean Interface

In this section, we will see how to implement MBean interfaces and will take a look at the application that is used throughout this book. Implementing the MBean interface is actually very straightforward, as we'll see.

The classes we will use in this chapter and their relationships to one another are shown in UML notation in Figure 2-7.

Figure 2-7. UML notation for the application classes used in this chapter

As we saw in design pattern #1, we must implement the MBean interface on the appropriately named class using the implements keyword. For the sake of review, let's take a quick look at how to do this. Example 2-2 showed the BasicMBean management interface definition. The syntax to implement this interface on the Basic class is:

public class Basic implements BasicMBean {
//. . .
}

Notice that this interface has only getters (i.e., all attributes are read-only). Because the implementation of the getters will be hidden behind the MBean interface, we are free to implement them however we choose, as long as the implicit contract provided by the interface is obeyed. However, the most common way to implement a getter is to declare a private instance variable on the class and simply return the value of that member variable when the getter is invoked:

When implementing the operation, we simply write code that makes the operation do something. Notice that the enableTracing() operation shown above resembles a setter. Why didn't we simply provide a setter for the TraceOn attribute? Notice that enableTracing() acts as a setter—it sets the value of the attribute—but with one
important difference: it only sets the attribute to true. A setter can set the value to any value (within language limits, of course) that is acceptable for the data type of the attribute.

Each child class of Basic must provide its own implementation of reset(). For this reason, there is a public setter (this setter also could have been protected or friendly) for the NumberOfResets attribute. Notice, however, that setNumberOfResets() is not included on the BasicMBean interface and hence is not part of the management interface of the MBean.

While simply returning the value of a member variable of the same type as the attribute for which the getter is provided is the most common way of writing a getter, it is not the only way. For example, without changing the interface, we might implement NumberOfResets as:

Notice that in this code snippet, the backing store for the NumberOfResets attribute is a java.lang.Integer object. When getNumberOfResets() is called, we simply return intValue() on the Integer object. Again, the MBean interface serves as the contract between the MBean server, a management application, and your MBean. As long as your MBean implementation obeys the MBean interface, you are free to implement the interface however you choose.

A getter doesn't have to be that simple, however. For example, a getter can also be a calculated quantity. Consider the declaration of the WorkerMBean interface in Example 2-3. Notice the read-only attribute AverageUnitProcessingTime, which is of type float:

In designing the sample code, I decided to calculate the elapsed system time required to process a workunit and accumulate it in a private instance variable of type long called _totalProcessingTime. Then, when getAverageUnitProcessingTime() is called, the average is calculated by dividing _totalProcessingTime by the number of units processed so far (taking care not to divide by zero if no units have been processed).

Consider the needs of your application before implementing your setters. Depending on how robust I want to make the implementation, the implementation of setWorkerName() shown above may be sufficient. However, I might want to set the value only if the new value is not a null reference, in which case I would make the following modification:

An example of a more complicated setter is setQueueSize(), for the Queue class. This setter allows a management application to dynamically alter the size of the queue, so, as you can imagine, it is not as straightforward as simply setting an attribute value. Here is the code for setQueueSize():

This code allows the queue to grow but not to shrink. Essentially, what this setter does is this: if activity in the queue is not currently suspended, and if the new queue size is greater than the current size, a new Object array is allocated and copied, then any threads in the wait state are signaled to become runnable. It's not too complicated, but it's certainly not as simple as just setting an instance variable's value.