MBeanInfo.equals: who's asking?
Defining an equals(Object) method in a public class is not always straightforward. One reason it might not be is that the answer to the question "are these objects equal?" might be "who's asking?".
In a very interesting comment on my previous blog entry, rullrich suggests that the real problem is with MBeanInfo.equals. I'm inclined to disagree, but the issue is a subtle one.
One frequent difficulty with implementing an
equals(Object) method is what to do in subclasses.
Josh Bloch has an exhaustive discussion of this question in Effective
Java, so I won't go into it in any more detail, except to
say that there is a blemish in the JMX API because it doesn't
follow Bloch's advice. The class OpenMBeanInfoSupport
is a subclass of MBeanInfo
and overrides its equals method. In addition to
the requirements of MBeanInfo.equals, it requires
that its argument be an instance of OpenMBeanInfo.
This means that this method violates the symmetry property
guaranteed by the contract for Object.equals.
If I have an object mbeanInfo of class
MBeanInfo and an object openMBeanInfo
of class OpenMBeanInfoSupport, then
mbeanInfo.equals(openMBeanInfo) might be true while
openMBeanInfo.equals(mbeanInfo) is false. Ugh.
But the question at hand is whether two MBeanInfo
objects should be equal if (for example) they contain the same
attributes but in a different order. This is where I say we need
to know: who's asking?
I can see at least two use cases for callers of
MBeanInfo.equals:
A client has been coded to interact with a particular sort of MBean, say the MemoryMXBean from
java.lang.management. It wants to check that the interface of the MBean it is interacting with is indeed the expected interface.A graphical client (such as JConsole) has created a GUI for a certain MBean and wants to know if the MBean's interface has changed so it can change the GUI.
The snapshot shows the JConsole GUI for the MemoryMXBean. If
the order of the attributes changed, and assuming we think it is
useful to preserve the order from the MBeanInfo, then we would
want to change the order in the GUI too. You could imagine
JConsole calling
mbeanServer.getMBeanInfo(memoryMXBeanName) and
comparing the MBeanInfo it gets back with the one it previously
used to construct this view.
In this second use case, MBeanInfo.equals should return false if the order of the attributes has changed, or if any other detail has changed, such as the description of an attribute. But in the first use case, MBeanInfo.equals should ignore these details because they don't change the set of valid client operations. Obviously we can't make MBeanInfo.equals conform to both use cases so we must pick one.
We chose the second use case because it is precise. Two MBeanInfo objects are different if they differ in any detail, including the order of attributes. The first use case is much less precise:
- Changing the type of an attribute from int to Integer has no effect on clients so should we consider two MBeanInfo objects equal if that is their only difference?
- What about changes to the Descriptor of an MBeanInfo or a contained MBeanAttributeInfo?
- Adding a new attribute to an MBean does not invalidate existing clients, but we can't consider two MBeanInfo objects equal if one of them has an attribute that the other doesn't. (It would violate symmetricity again.)
A secondary reason for preferring the definition where order is
significant is that it makes for a faster equals
method. If order were not significant then we would need either
a quadratic algorithm or the creation of extra objects inside
the method. This is very much a secondary reason;
efficiency concerns should almost never be the primary
motivation for API design choices. Correctness
and simplicity trump efficiency!
A summary of the choice we made, then, is that it allows me to answer the question: Is this MBeanInfo object the same as one I got earlier for the same MBean? If the answer is yes, then the MBean interface definitely hasn't changed. If the answer is no, then the MBean interface might or might not have changed, but I must assume it has.
The choice does not in general allow you to use MBeanInfo.equals to answer the incompatible question: Is this MBeanInfo object the same as the one I expected?
So the moral is that when you define an equals
method you must know what question you want it to answer.
But what about the order of methods in a Standard MBean interface?
rullrich also makes some other interesting points.
First, in principle calling getMBeanInfo() twice on
the same MBean might return a different object each time, with a
different attribute order, even if the MBean has not changed in
any way. This is not a problem with the JDK, but you could
imagine an implementation that (a) does not cache the MBeanInfo
for a Standard MBean interface (as the JDK does), and where (b)
the order of the methods returned by Class.getMethods() can vary
at different times for the same Class object (which
it cannot in the JDK). Well, I think a JMX implementation
running on a JVM with property (b) had better ensure that it
does in fact cache the MBeanInfo. We could certainly modify the
spec so it says this explicitly, but I think we would be
addressing a very theoretical problem.
Second, I am also inclined to think we should preserve the order returned by Class.getMethods(). The implication for the regression test is that it should use reflection to determine what it thinks the order of attributes and operations in the MBeanInfo should be, and compare this with the order that is actually in the MBeanInfo returned by the JMX API. That test is then imposing a stronger restriction on the JDK's implementation of the API than is required by the spec, but that's all right. The bug in the test that I actually coded is that it hardwired this expected order instead of getting it from Class.getMethods().
- Login or register to post comments
- Printer-friendly version
- emcmanus's blog
- 640 reads





