我正在尝试创建一个MXBean操作,该操作将返回一个没有属性的抽象类型(实际类型及其属性将在运行时确定)。我的数据模型可以简单地放如下:
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();
}
该操作可以返回具有foo
属性的A
实例,也可以返回具有bar
属性的B
实例。当然,当我尝试注册MBean
实例时,我会看到一个闪亮的NotCompliantMBeanException
:
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)
似乎我可以用常规的 MBean
s 和 Serializable
做一些事情,但不能用 MXBean
s 和 CompositeDataView
做。还是我错了?
可以或多或少地做你想做的事情,尽管不是那么简单。关键点是MXBean规范要求复合数据至少有一个项目。您可以通过在基类中type
属性来满足此要求,我在这里称之为AnyCompositeData
。type
属性还可以用于决定如何将CompositeData
转换回特定类型,例如Foo
和Bar
。在这里的代码中,我将所有内容都塞进了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)
方法知道所有的子类。