|
|
||
Eamonn McManus's BlogCustom types for MXBeansPosted by emcmanus on May 30, 2007 at 07:00 AM | Comments (0)MXBeans map between arbitrary Java types and a fixed set of types in javax.management.openmbean called the Open Types. This allows clients to interact with MXBeans, without needing to know the original Java types (which might require putting extra jars in their classpath and so on). Up until now the mapping rules were fixed. Certain types can not be mapped by these rules, for example self-referential types or types such as Object or Number. In the Java 7 platform, we're planning to allow customization of the rules, as part of JSR 255 which is defining version 2.0 of the JMX API. This is a summary of the proposed changes. What problem are we solving?There are three sorts of use cases here:
The proposal is to add one class, one interface, and two annotations to address these cases. The existing class javax.management.StandardMBean acquires two new constructors and the existing class javax.management.JMX acquires two new overloads of existing methods. MXBeanMapping and @MXBeanMappingClassThe most important change is the new class javax.management.openmbean.MXBeanMapping: public abstract class MXBeanMapping { Suppose we want to define a mapping for the class MyLinkedList, which looks like this: public class MyLinkedList { This is not a valid type for MXBeans, because it contains a self-referential property "next" defined by the getNext() method. (This example comes from Simone Bordet.) So we would like to specify a mapping for it explicitly. When an MXBean interface contains MyLinkedList, that will be mapped into a String[], which is a valid Open Type. To define this mapping, we first subclass MXBeanMapping: public class MyLinkedListMapping extends MXBeanMapping { @Override @Override The call to the superclass constructor specifies what the original Java type is (MyLinkedList.class) and what Open Type it is mapped to (ArrayType.getArrayType(SimpleType.STRING)). The fromOpenValue method says how we go from the Java type to the Open Type, and the toOpenValue method says how we go from the Open Type to the Java type. With this mapping defined, we can annotate MyLinkedList with the new annotation @javax.management.openmbean.MXBeanMappingClass: @MXBeanMappingClass(MyLinkedListMapping.class) Now we can use MyLinkedList in an MXBean interface and it will work. This satisfies use case 1 above. MXBeanMappingFactory and @MXBeanMappingFactoryClassIf we are unable to annotate individual classes, then we can define a mapping factory that is consulted every time a type needs to be mapped. This is also useful if we would like to apply the same set of rules across a whole set of classes (for example, any class that implements List<E> is mapped in the same way as List<E>). A mapping factory is a subclass of javax.management.openmbean.MXBeanMappingFactory: public abstract class MXBeanMappingFactory { For example, suppose we can't change MyLinkedList, so we can't add the @MXBeanMappingClass annotation to it. We can achieve the same effect by defining a mapping factory as follows: public class MyLinkedListMappingFactory extends
MXBeanMappingFactory { Now we can add the new annotation javax.management.openmbean.MXBeanMappingFactoryClass to any MXBean interface that references MyLinkedList: @MXBeanMappingFactoryClass(MyLinkedListMappingFactory.class) This satisfies use case 2 above. New StandardMBean constructors and JMX.newMXBeanProxy methodThe existing class StandardMBean is used to make instances of Standard MBeans and MXBeans when you need to control some aspects of their behaviour, such as the descriptions in the MBeanInfo. We can extend the set of constructors as follows: public class StandardMBean implements DynamicMBean { The options parameter allows us to specify a number of potentially interesting things:
The question of what the MBeanOptions type is is still open. It could be a Map<String, ?> where each option is represented by a string constant. This is the approach taken by the JMX Remote API, for example. Or, it could be a special-purpose class called MBeanOptions with methods to set each of the options. I plan to write more on this later; for now I'll assume it's the MBeanOptions option. If you need to create an MXBean that implements the SomethingMXBean interface above and uses the MyLinkedListMappingFactory, but you can't add an annotation to SomethingMXBean, then you can do so using either of the two existing ways to use StandardMBean, subclassing or delegation, but supplying an option: MBeanOptions options = MBeanOptions.DEFAULT.withMXBeanMappingFactory( If you have a custom mapping in your MBean server, then you need the same custom mapping in a client if the client is making a proxy. So we add another overloading of JMX.newMXBeanProxy: public class JMX { Using this, you can create a proxy like this: SomethingMXBean proxy = JMX.newMXBeanProxy( Here the options object can be the same as before. This satisfies use case 3 above. Interoperation when there are custom typesMXBeans map arbitrary Java types to Open Types. The addition of custom mappings doesn't change this - the result still has to be Open Types. So for generic clients like JConsole, nothing changes when custom types are added into the mix. A client that is aware of the MXBean interfaces in use (like SomethingMXBean) can construct a proxy. To do that, it must have the interface available. If the interface has a @MXBeanMappingFactoryClass annotation, or if it contains a type that has a @MXBeanMappingClass annotation, then the classes referenced by those annotations must be present in the client too. It usually isn't any more difficult to arrange for the mapping classes to be present than to arrange for the original MXBean interface to be present. If the mapping has been specified on the server using an explicit MXBeanMappingFactory, then the same or an equivalent factory must be used on the client. This is the case where there is the most risk of inconsistency. To help check that client and server are using the same MXBeanMappingFactory, the Descriptor of an MXBean using an MXBeanMappingFactory will contain a field naming the factory class, MyLinkedListMappingFactory in the examples above. Some detailsHere are some details I omitted above for clarity. The methods toOpenValue and fromOpenValue have inconsistent throws clauses. This reflects a similar inconsistency in the MXBean spec, itself due to historical reasons. The Java type and Open Type in MXBeanMapping must be supplied to the constructor by the subclass, and cannot be changed thereafter. This could be a limitation but I cannot currently see any cases where you would not be able to supply values to the super-constructor call. MXBeanMapping is a class and not an interface for a number of reasons, notably that it allows us to have final methods (getOpenType, getOpenClass, getJavaType) and methods with default implementations (checkReconstructible). MXBeanMappingFactory is a class not an interface so that we can add methods to it in later versions of the API if necessary. The method MXBeanMapping.checkReconstructible() is used to determine if it is possible to map back from a value of the OpenType to a value of the original Java type. The default implementation does nothing. A subclass can override this method to throw OpenDataException if this mapping is not possible. See the MXBean specification for a discussion of reconstructible types. MXBeanMappingClass can be defined like this:
The signature of MXBeanMappingFactory.mappingForType is this:
Open questionsThis is still a draft proposal, and some questions remain. As I mentioned above, the exact type of the MBeanOptions parameter to various methods is yet to be determined. Should the @MXBeanMappingFactory option be inherited? For example, if you define public interface SubSomethingMXBean extends SomethingMXBean {...} should you inherit the @MXBeanMappingFactory annotation from SomethingMXBean? I think the answer must be yes, but what if SubSomethingMXBean inherits from more than one MXBean interface, and they have different @MXBeanMappingFactory annotations? Should the @MXBeanMappingFactory option be applicable to packages? A reminder that you can annotate packages by creating a file called package-info.java in the package with contents like this: @javax.management.openmbean.MXBeanMappingFactory(MyLinkedListMappingFactory.class) If @MXBeanMappingFactory can apply to packages and is also inherited from superinterfaces then we may have some complicated rules for precedence between the two. ConclusionIn conclusion, we're specifying something quite simple: how to extend the standard MXBean mappings with custom mappings. But the details turn out not to be so simple! AcknowledgementsThis specification has been discussed in the JSR 255 Expert Group. Mandy Chung also had some very helpful comments. Bookmark blog post: CommentsComments are listed in date ascending order (oldest first) | Post Comment | ||
|
|