Skip to main content

MBeanInfo.equals: who's asking?

Posted by emcmanus on November 13, 2006 at 9:40 AM PST

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 href="http://weblogs.java.net/blog/emcmanus/archive/2006/11/notes_on_unspec.html#20251">comment
on my href="http://weblogs.java.net/blog/emcmanus/archive/2006/11/notes_on_unspec.html">previous
blog entry, rullrich suggests that the real problem
is with href="http://download.java.net/jdk6/doc/api/javax/management/MBeanInfo.html#equals(java.lang.Object)">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 href="http://java.sun.com/docs/books/effective/">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 href="http://download.java.net/jdk6/doc/api/javax/management/openmbean/OpenMBeanInfoSupport.html">OpenMBeanInfoSupport
is a subclass of href="http://download.java.net/jdk6/doc/api/javax/management/MBeanInfo.html">MBeanInfo
and overrides its equals method. In addition to
the requirements of MBeanInfo.equals, it requires
that its argument be an instance of href="http://download.java.net/jdk6/doc/api/javax/management/openmbean/OpenMBeanInfo.html">OpenMBeanInfo.
This means that this method violates the symmetry property
guaranteed by the contract for href="http://download.java.net/jdk6/doc/api/java/lang/Object.html#equals(java.lang.Object)">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:

  1. A client has been coded to interact with a particular
    sort of MBean, say the href="http://download.java.net/jdk6/doc/api/java/lang/management/MemoryMXBean.html">MemoryMXBean
    from java.lang.management. It wants to check
    that the interface of the MBean it is interacting with is
    indeed the expected interface.

  2. A graphical client (such as href="http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html">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.

JConsole GUI for MemoryMXBean src="http://weblogs.java.net/blog/emcmanus/archive/jconsole-memorymxbean.png"
width="534" height="482" />

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 href="http://download.java.net/jdk6/doc/api/javax/management/Descriptor.html">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. href="http://www.artima.com/weblogs/viewpost.jsp?thread=142428">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().

Related Topics >>