2 Replies Latest reply: Jun 15, 2010 4:20 PM by 843798 RSS

    Generic Attribute in MXBean

    843798
      Hi,

      I'm trying to register an MXBean (using Java 1.6) that has a get attribute that returns a generic class with the type specified in the interface. I get a non-compliant mbean exception thrown indicating that the method in question returns a type that cannot be translated into an open type.

      Here is some code that illustrates what I am trying to do:
          public static class GenericValue<E>
          {
              private final E val;
      
              GenericValue(E val) {
                  this.val = val;
              }
      
              public E getVal() {
                  return val;
              }
          }
      
          public interface GenericHolderMXBean {
              public GenericValue<Float> getVal();
          }
      
          public static class GenericHolder
                  implements GenericHolderMXBean {
      
              @Override
              public GenericValue<Float> getVal() {
                  return new GenericValue<Float>(1.0f);
              }
          }
      
          ...
          TestMbeanStuff.GenericHolder genericHolder = new GenericHolder();
          MBeanServer m = ManagementFactory.getPlatformMBeanServer();
          ObjectName name = new ObjectName("com.test:type=GenericHolder");
          m.registerMBean( genericHolder, name )
      Is there a way to make this work? It seems analogous to List working as an attribute in an MXBean, but perhaps support for collections is a special case made by JMX and it is not possible to use one's own generic types?

      Thanks,
      Ryan
        • 1. Re: Generic Attribute in MXBean
          800572
          Ryan,

          That is exactly right. Support for collections is a special case, and there is unfortunately no way to achieve what you want. The reason is that complex types like GenericValue are translated into CompositeType, and there is no provision in CompositeType for type parameters. I think about the best you can do is to define a sort of union type that has one field for every type you might want, only one of which will be non-null for any given instance. That's admittedly not very pretty.

          Regards,
          Éamonn McManus -- JMX Spec Lead -- [http://weblogs.java.net/blog/emcmanus]
          • 2. Re: Generic Attribute in MXBean
            843798
            Thanks for your response Éamonn,

            I do have a bit of a follow-up question. My original question was a simplification of what I would like to do. What I'm really after is having my MXBean interface return a type that has a generic type nested as an attribute. eg:
                public static class GenericHolder {
            
                    public GenericValue<Float> getVal() {
                        return new GenericValue<Float>(1.0f);
                    }
                }
            
                public interface NestedGenericHolderMXBean {
                    public GenericHolder getVal();
                }
            
                public static class NestedGenericHolder
                        implements NestedGenericHolderMXBean {
                    public GenericHolder getVal() {
                        return new GenericHolder();
                    }
                }
            I would still like to make this work and can see a way to make it work. But, there are a couple of ugly things about my solution. I can probably live with them, but would like to verify that they're unavoidable before I do.

            NestedGenericHolder, as above, cannot be registered any more than my original example (for the same reason). My first thought is to make GenericHolder implement CompositeDataView and define how it should be converted to CompositeData. The modified GenericHolder looks something like:
                public static class GenericHolder implements CompositeDataView {
            
                    public GenericValue<Float> getGenericVal() {
                        return new GenericValue<Float>(1.0f);
                    }
            
                    @Override
                    public CompositeData toCompositeData(CompositeType ct) {
                        try {
                            List<String> itemNames = new ArrayList<String>();
                            List<String> itemDescriptions = new ArrayList<String>();
                            List<OpenType<?>> itemTypes = new ArrayList<OpenType<?>>();
                            itemNames.add("genericVal");
                            itemTypes.add(SimpleType.FLOAT);
                            itemDescriptions.add("generic value");
                            CompositeType xct =
                                    new CompositeType(
                                            ct.getTypeName(),
                                        ct.getDescription(),
                                        itemNames.toArray(new String[0]),
                                        itemDescriptions.toArray(new String[0]),
                                        itemTypes.toArray(new OpenType<?>[0]));
            
                            CompositeData cd = new CompositeDataSupport(
                                    xct,
                                    new String[] { "genericVal" },
                                    new Object[] {
                                            getGenericVal().getVal()
                                    });
                            assert ct.isValue(cd); // check we've done it right
                            return cd;
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            This doesn't help anything though because introspection still seems to inspect this class' attributes. ie: Even though I've manually defined how to convert to CompositeData, it still seems to want to verify that it could do it automatically if it had to. Anyway, my next fix, is to change the name of the "getGenericValue" method to "genericValue" so that JMX doesn't expect that "genericVal" should be an attribute of my type. (And then I will add it to the CompositeData as an extra item - as described in the CompositeDataView JavaDoc).

            Registration now because (I believe) JMX refuses to convert an object to a CompositeData that contains no attributes. As a result, I attempt to fix this by adding another getter to my class:
                public static class GenericHolder implements CompositeDataView {
            
                    // MXBean introspection rejects classes with no attributes.
                    public Integer getOtherVal() {
                        return 17;
                    }
            
                    public GenericValue<Float> genericVal() {
                        return new GenericValue<Float>(1.0f);
                    }
            
                    @Override
                    public CompositeData toCompositeData(CompositeType ct) {
                        try {
                            List<String> itemNames = new ArrayList<String>(ct.keySet());
                            List<String> itemDescriptions = new ArrayList<String>();
                            List<OpenType<?>> itemTypes = new ArrayList<OpenType<?>>();
                            for (String item : itemNames) {
                                itemDescriptions.add(ct.getDescription(item));
                                itemTypes.add(ct.getType(item));
                            }
            
                            itemNames.add("genericVal");
                            itemTypes.add(SimpleType.FLOAT);
                            itemDescriptions.add("generic value");
                            CompositeType xct =
                                    new CompositeType(
                                            ct.getTypeName(),
                                        ct.getDescription(),
                                        itemNames.toArray(new String[0]),
                                        itemDescriptions.toArray(new String[0]),
                                        itemTypes.toArray(new OpenType<?>[0]));
            
                            CompositeData cd = new CompositeDataSupport(
                                    xct,
                                    new String[] { "otherVal", "genericVal" },
                                    new Object[] {
                                            getOtherVal(),
                                            genericVal().getVal()
                                    });
                            assert ct.isValue(cd); // check we've done it right
                            return cd;
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            It now works, but not without a few compromises:

            1. I have to implement toCompositeData(). This one isn't too big of a problem. I can certainly live with this.

            2. I have to avoid method names that JMX will interpret as attributes when the return type are ones that it doesn't know how to convert to an OpenType. (I then add them into my MXBean, by adding them into the CompositeDataView I build.)

            3. Such classes (GenericHolder) must contain at least one attribute with a type that can automatically be converted to an Open type.



            I'd just like to make sure there aren't better solutions to the problems I described above.

            Thanks and sorry for the overly long post,
            Ryan