MXBean操作签名中的抽象类型

时间:2014-12-08 10:42:03

标签: java jmx mxbean

我正在尝试创建一个MXBean操作,该操作将返回一个抽象类型w / o属性(实际类型及其属性将在运行时确定)。我的数据模型可以简单地说如下:

public interface I extends CompositeDataView {
    // empty
}

public final class A implements I {
    private final String foo;

    @ConstructorProperties({"foo"})
    public A(final String foo) {/* ... */}

    public String getFoo() {/* ... */}

    @Override
    public CompositeData toCompositeData(CompositeType ct) {/* ... */}

    public static A from(final CompositeData cd) {/* ... */}
}

public final class B implements I {
    private final String bar;

    @ConstructorProperties({"bar"})
    public B(final String bar) {/* ... */}

    public String getBar() {/* ... */}

    @Override
    public CompositeData toCompositeData(CompositeType ct) {/* ... */}

    public static B from(final CompositeData cd) {/* ... */}
}

...并且MXBean操作签名是:

@MXBean
public interface Baz {
    I f();
}

该操作可以返回具有A属性的foo实例,或具有B属性的bar实例。 当然,我立即向我提出了一个闪亮的NotCompliantMBeanException我尝试注册MBean实例:

Caused by: javax.management.openmbean.OpenDataException: Can't map I to an open data type
    at com.sun.jmx.mbeanserver.DefaultMXBeanMappingFactory.makeCompositeMapping(DefaultMXBeanMappingFactory.java:458)
    at com.sun.jmx.mbeanserver.DefaultMXBeanMappingFactory.makeMapping(DefaultMXBeanMappingFactory.java:292)
    at com.sun.jmx.mbeanserver.DefaultMXBeanMappingFactory.mappingForType(DefaultMXBeanMappingFactory.java:257)

似乎我可以使用常规MBeanSerializable执行某些操作,但不能使用MXBeanCompositeDataView。或者我错了吗?

1 个答案:

答案 0 :(得分:1)

可以做多或少或多或少的事情,尽管不是那么简单。关键是MXBean规范要求CompositeData至少有一个项目。您可以通过在基类中使用属​​性type来满足此要求,我在此处将其称为AnyCompositeDatatype属性还可用于决定如何从CompositeData转换回特定类型,例如FooBar。在这里的代码中,我将所有内容都填入了AnyCompositeData类,但更实际的是它当然是单独的类。我只详细说明了具体的类Foo,但显然如何扩展模式以支持其他类。

public abstract class AnyCompositeData implements CompositeDataView {
  private final String type;

  public AnyCompositeData(String type) {
    this.type = type;
  }

  public String getType() {
    return type;
  }

  public static AnyCompositeData from(CompositeData cd) {
    switch ((String) cd.get("type")) {
      case "Foo":
        return new Foo((String) cd.get("foo"));
      default:
        throw new IllegalArgumentException("Don't know how to reconstruct: " + cd.get("type"));
    }
  }

  public static class Foo extends AnyCompositeData {
    private final String foo;

    Foo(String foo) {
      super("Foo");
      this.foo = foo;
    }

    public String getFoo() {
      return foo;
    }

    @Override
    public CompositeData toCompositeData(CompositeType ct) {
      try {
        String[] items = {"type", "foo"};
        OpenType<?>[] itemTypes = {SimpleType.STRING, SimpleType.STRING};
        Object[] itemValues = {"Foo", foo};
        CompositeType compositeType = new CompositeType("Foo", "Foo", items, items, itemTypes);
        return new CompositeDataSupport(compositeType, items, itemValues);
      } catch (OpenDataException e) {
        throw new RuntimeException(e);
      }
    }
  }

  @MXBean
  public interface Baz {
    AnyCompositeData f();
  }

  static class BazImpl implements Baz {
    @Override
    public AnyCompositeData f() {
      return new Foo("whatever");
    }
  }

  public static void main(String[] args) throws Exception {
    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    ObjectName objectName = new ObjectName("test:baz=baz");
    mbs.registerMBean(new BazImpl(), objectName);
    Baz bazProxy = JMX.newMXBeanProxy(mbs, objectName, Baz.class);
    AnyCompositeData result = bazProxy.f();
    assert result instanceof Foo;
    assert ((Foo) result).getFoo().equals("whatever");
  }
}

如果您有很多子类,如Foo,那么您可能需要考虑以某种方式使用反射,而不是让from(CompositeData)方法了解所有子类。