Reputation: 5328
I'm trying to create an MXBean operation which would return an abstract type w/o properties (the actual type and its attributes are to be determined at run time). My data model can be simply put as follows:
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) {/* ... */}
}
... and the MXBean operation signature is:
@MXBean
public interface Baz {
I f();
}
The operation can return either an instance of A
with a foo
attribute, or an instance of B
with a bar
attribute.
Of course I'm presented with a shiny NotCompliantMBeanException
immediately I try to register the MBean
instance:
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)
It seems there's something I can do with regular MBean
s and Serializable
but can't with MXBean
s and CompositeDataView
. Or am I wrong?
Upvotes: 2
Views: 436
Reputation: 363
It is possible to do more or less what you want, though not all that straightforward. The key point is that the MXBean spec requires a CompositeData to have at least one item. You can satisfy this requirement by having a property type
in your base class, which I've called AnyCompositeData
here. The type
property can also serve to decide how to translate back from CompositeData
to the specific types such as your Foo
and Bar
. In the code here I stuffed everything into the AnyCompositeData
class, though more realistically it would be separate classes of course. I only spelled out the concrete class Foo
but it should be obvious how to extend the pattern to support other classes.
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");
}
}
If you had a lot of subclasses like Foo
, then you might want to consider using reflection in some way rather than having the from(CompositeData)
method know about all the subclasses.
Upvotes: 1