();
+
+ final MBeanServerConnection mbsc;
}
diff --git a/src/share/classes/com/sun/jmx/mbeanserver/MXBeanProxy.java b/src/share/classes/com/sun/jmx/mbeanserver/MXBeanProxy.java
index 6ecca2ce1d58526d5592cd7bf954770d4ff5a90c..16aab47c2157a795ab02f77672faa4b0ea4b2bba 100644
--- a/src/share/classes/com/sun/jmx/mbeanserver/MXBeanProxy.java
+++ b/src/share/classes/com/sun/jmx/mbeanserver/MXBeanProxy.java
@@ -27,14 +27,15 @@ package com.sun.jmx.mbeanserver;
import static com.sun.jmx.mbeanserver.Util.*;
-import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import javax.management.Attribute;
import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
+import javax.management.openmbean.MXBeanMappingFactory;
/**
Helper class for an {@link InvocationHandler} that forwards methods from an
@@ -46,8 +47,7 @@ import javax.management.ObjectName;
@since 1.6
*/
public class MXBeanProxy {
- public MXBeanProxy(Class> mxbeanInterface)
- throws IllegalArgumentException {
+ public MXBeanProxy(Class> mxbeanInterface, MXBeanMappingFactory factory) {
if (mxbeanInterface == null)
throw new IllegalArgumentException("Null parameter");
@@ -55,14 +55,15 @@ public class MXBeanProxy {
final MBeanAnalyzer analyzer;
try {
analyzer =
- MXBeanIntrospector.getInstance().getAnalyzer(mxbeanInterface);
+ MXBeanIntrospector.getInstance(factory).getAnalyzer(mxbeanInterface);
} catch (NotCompliantMBeanException e) {
throw new IllegalArgumentException(e);
}
analyzer.visit(new Visitor());
}
- private class Visitor implements MBeanAnalyzer.MBeanVisitor {
+ private class Visitor
+ implements MBeanAnalyzer.MBeanVisitor {
public void visitAttribute(String attributeName,
ConvertingMethod getter,
ConvertingMethod setter) {
@@ -160,10 +161,29 @@ public class MXBeanProxy {
Handler handler = handlerMap.get(method);
ConvertingMethod cm = handler.getConvertingMethod();
- MXBeanLookup lookup = MXBeanLookup.lookupFor(mbsc);
- Object[] openArgs = cm.toOpenParameters(lookup, args);
- Object result = handler.invoke(mbsc, name, openArgs);
- return cm.fromOpenReturnValue(lookup, result);
+ String prefix = extractPrefix(name);
+ MXBeanLookup lookup = MXBeanLookup.lookupFor(mbsc, prefix);
+ MXBeanLookup oldLookup = MXBeanLookup.getLookup();
+ try {
+ MXBeanLookup.setLookup(lookup);
+ Object[] openArgs = cm.toOpenParameters(lookup, args);
+ Object result = handler.invoke(mbsc, name, openArgs);
+ return cm.fromOpenReturnValue(lookup, result);
+ } finally {
+ MXBeanLookup.setLookup(oldLookup);
+ }
+ }
+
+ private static String extractPrefix(ObjectName name)
+ throws MalformedObjectNameException {
+ String domain = name.getDomain();
+ int slashslash = domain.lastIndexOf("//");
+ if (slashslash > 0 && domain.charAt(slashslash - 1) == '/')
+ slashslash--;
+ if (slashslash >= 0)
+ return domain.substring(0, slashslash + 2);
+ else
+ return null;
}
private final Map handlerMap = newMap();
diff --git a/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java b/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java
index 977c74a3230fb7139f57eb13b5622992923d4a36..cb8587450b83d146946541d15397745ddb451101 100644
--- a/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java
+++ b/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java
@@ -35,6 +35,8 @@ import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
+import javax.management.openmbean.MXBeanMappingFactory;
+import javax.management.openmbean.MXBeanMappingFactoryClass;
/**
* Base class for MXBeans.
@@ -61,14 +63,16 @@ public class MXBeanSupport extends MBeanSupport {
if it does not implement the class {@code mxbeanInterface} or if
that class is not a valid MXBean interface.
*/
- public MXBeanSupport(T resource, Class mxbeanInterface)
+ public MXBeanSupport(T resource, Class mxbeanInterface,
+ MXBeanMappingFactory mappingFactory)
throws NotCompliantMBeanException {
- super(resource, mxbeanInterface);
+ super(resource, mxbeanInterface, mappingFactory);
}
@Override
- MBeanIntrospector getMBeanIntrospector() {
- return MXBeanIntrospector.getInstance();
+ MBeanIntrospector
+ getMBeanIntrospector(MXBeanMappingFactory mappingFactory) {
+ return MXBeanIntrospector.getInstance(mappingFactory);
}
@Override
@@ -76,8 +80,7 @@ public class MXBeanSupport extends MBeanSupport {
return mxbeanLookup;
}
- static Class> findMXBeanInterface(Class> resourceClass)
- throws IllegalArgumentException {
+ static Class super T> findMXBeanInterface(Class resourceClass) {
if (resourceClass == null)
throw new IllegalArgumentException("Null resource class");
final Set> intfs = transitiveInterfaces(resourceClass);
@@ -104,7 +107,7 @@ public class MXBeanSupport extends MBeanSupport {
throw new IllegalArgumentException(msg);
}
if (candidates.iterator().hasNext()) {
- return candidates.iterator().next();
+ return Util.cast(candidates.iterator().next());
} else {
final String msg =
"Class " + resourceClass.getName() +
@@ -116,7 +119,7 @@ public class MXBeanSupport extends MBeanSupport {
/* Return all interfaces inherited by this class, directly or
* indirectly through the parent class and interfaces.
*/
- private static Set> transitiveInterfaces(Class c) {
+ private static Set> transitiveInterfaces(Class> c) {
Set> set = newSet();
transitiveInterfaces(c, set);
return set;
@@ -127,7 +130,7 @@ public class MXBeanSupport extends MBeanSupport {
if (c.isInterface())
intfs.add(c);
transitiveInterfaces(c.getSuperclass(), intfs);
- for (Class sup : c.getInterfaces())
+ for (Class> sup : c.getInterfaces())
transitiveInterfaces(sup, intfs);
}
@@ -157,12 +160,7 @@ public class MXBeanSupport extends MBeanSupport {
// eventually we could have some logic to supply a default name
synchronized (lock) {
- if (this.objectName != null) {
- final String msg =
- "MXBean already registered with name " + this.objectName;
- throw new InstanceAlreadyExistsException(msg);
- }
- this.mxbeanLookup = MXBeanLookup.lookupFor(server);
+ this.mxbeanLookup = MXBeanLookup.Plain.lookupFor(server);
this.mxbeanLookup.addReference(name, getResource());
this.objectName = name;
}
@@ -171,12 +169,20 @@ public class MXBeanSupport extends MBeanSupport {
@Override
public void unregister() {
synchronized (lock) {
- if (mxbeanLookup.removeReference(objectName, getResource()))
- objectName = null;
+ if (mxbeanLookup != null) {
+ if (mxbeanLookup.removeReference(objectName, getResource()))
+ objectName = null;
+ }
+ // XXX: need to revisit the whole register/unregister logic in
+ // the face of wrapping. The mxbeanLookup!=null test is a hack.
+ // If you wrap an MXBean in a MyWrapperMBean and register it,
+ // the lookup table should contain the wrapped object. But that
+ // implies that MyWrapperMBean calls register, which today it
+ // can't within the public API.
}
}
+ private final Object lock = new Object(); // for mxbeanLookup and objectName
- private Object lock = new Object(); // for mxbeanLookup and objectName
- private MXBeanLookup mxbeanLookup;
+ private MXBeanLookup.Plain mxbeanLookup;
private ObjectName objectName;
}
diff --git a/src/share/classes/com/sun/jmx/mbeanserver/NotificationMBeanSupport.java b/src/share/classes/com/sun/jmx/mbeanserver/NotificationMBeanSupport.java
index f1e3a13c891c1d4a62b24dbe96ce156fefaddbfd..c412207dcfd24b9cbe32522c554eac549fe258b3 100644
--- a/src/share/classes/com/sun/jmx/mbeanserver/NotificationMBeanSupport.java
+++ b/src/share/classes/com/sun/jmx/mbeanserver/NotificationMBeanSupport.java
@@ -30,6 +30,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.management.NotCompliantMBeanException;
import javax.management.Notification;
+import javax.management.openmbean.MXBeanMappingFactory;
/**
* A variant of {@code StandardMBeanSupport} where the only
@@ -48,7 +49,7 @@ public class NotificationMBeanSupport extends StandardMBeanSupport {
}
@Override
- MBeanIntrospector getMBeanIntrospector() {
+ MBeanIntrospector getMBeanIntrospector(MXBeanMappingFactory ignored) {
return introspector;
}
diff --git a/src/share/classes/com/sun/jmx/mbeanserver/PerInterface.java b/src/share/classes/com/sun/jmx/mbeanserver/PerInterface.java
index d20fe5f52392b612b1c1f5e5aad17927cc514f32..ef34a8f2991de88b6390157c894ae7ec0f68657c 100644
--- a/src/share/classes/com/sun/jmx/mbeanserver/PerInterface.java
+++ b/src/share/classes/com/sun/jmx/mbeanserver/PerInterface.java
@@ -231,7 +231,7 @@ final class PerInterface {
/**
* Visitor that sets up the method maps (operations, getters, setters).
*/
- private class InitMaps implements MBeanAnalyzer.MBeanVisitor {
+ private class InitMaps implements MBeanAnalyzer.MBeanVisitor {
public void visitAttribute(String attributeName,
M getter,
M setter) {
diff --git a/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java b/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java
index fd99162f38472705bf8046eb2d36807e21a849b1..131c5341b165f714e98da837a7cf26e77168b913 100644
--- a/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java
+++ b/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java
@@ -25,14 +25,13 @@
package com.sun.jmx.mbeanserver;
-import static com.sun.jmx.mbeanserver.Util.*;
-
import java.lang.reflect.Method;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
+import javax.management.openmbean.MXBeanMappingFactory;
/**
* Base class for Standard MBeans.
@@ -61,11 +60,11 @@ public class StandardMBeanSupport extends MBeanSupport {
*/
public StandardMBeanSupport(T resource, Class mbeanInterface)
throws NotCompliantMBeanException {
- super(resource, mbeanInterface);
+ super(resource, mbeanInterface, (MXBeanMappingFactory) null);
}
@Override
- MBeanIntrospector getMBeanIntrospector() {
+ MBeanIntrospector getMBeanIntrospector(MXBeanMappingFactory ignored) {
return StandardMBeanIntrospector.getInstance();
}
diff --git a/src/share/classes/javax/management/JMX.java b/src/share/classes/javax/management/JMX.java
index d2bc522afa1af7fcbff68f22d07aeb054b5554cb..91e9f455f60fdbe2a3c3958e5f4ef42a4322ae86 100644
--- a/src/share/classes/javax/management/JMX.java
+++ b/src/share/classes/javax/management/JMX.java
@@ -26,8 +26,17 @@
package javax.management;
import com.sun.jmx.mbeanserver.Introspector;
+import com.sun.jmx.remote.util.ClassLogger;
+import java.beans.BeanInfo;
+import java.beans.PropertyDescriptor;
+import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import java.util.Map;
+import java.util.TreeMap;
+import javax.management.openmbean.MXBeanMappingFactory;
/**
* Static methods from the JMX API. There are no instances of this class.
@@ -39,6 +48,8 @@ public class JMX {
* this class.
*/
static final JMX proof = new JMX();
+ private static final ClassLogger logger =
+ new ClassLogger("javax.management.misc", "JMX");
private JMX() {}
@@ -84,6 +95,14 @@ public class JMX {
*/
public static final String MXBEAN_FIELD = "mxbean";
+ /**
+ * The name of the
+ * {@code
+ * mxbeanMappingFactoryClass} field.
+ */
+ public static final String MXBEAN_MAPPING_FACTORY_CLASS_FIELD =
+ "mxbeanMappingFactoryClass";
+
/**
* The name of the {@code
* openType} field.
@@ -96,6 +115,264 @@ public class JMX {
*/
public static final String ORIGINAL_TYPE_FIELD = "originalType";
+ /**
+ * Options to apply to an MBean proxy or to an instance of {@link
+ * StandardMBean}.
+ *
+ * For example, to specify a custom {@link MXBeanMappingFactory}
+ * for a {@code StandardMBean}, you might write this:
+ *
+ *
+ * MXBeanMappingFactory factory = new MyMXBeanMappingFactory();
+ * JMX.MBeanOptions opts = new JMX.MBeanOptions();
+ * opts.setMXBeanMappingFactory(factory);
+ * StandardMBean mbean = new StandardMBean(impl, intf, opts);
+ *
+ *
+ * @see javax.management.JMX.ProxyOptions
+ */
+ public static class MBeanOptions implements Serializable, Cloneable {
+ private static final long serialVersionUID = -6380842449318177843L;
+
+ static final MBeanOptions MXBEAN = new MBeanOptions();
+ static {
+ MXBEAN.setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT);
+ }
+
+ private MXBeanMappingFactory mappingFactory;
+
+ /**
+ * Construct an {@code MBeanOptions} object where all options have
+ * their default values.
+ */
+ public MBeanOptions() {}
+
+ @Override
+ public MBeanOptions clone() {
+ try {
+ return (MBeanOptions) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /**
+ * True if this is an MXBean proxy or a StandardMBean instance
+ * that is an MXBean. The default value is false.
+ *
+ * This method is equivalent to {@link #getMXBeanMappingFactory()
+ * this.getMXBeanMappingFactory()}{@code != null}.
+ *
+ * @return true if this is an MXBean proxy or a StandardMBean instance
+ * that is an MXBean.
+ */
+ public boolean isMXBean() {
+ return (this.mappingFactory != null);
+ }
+
+ /**
+ * The mappings between Java types and Open Types to be used in
+ * an MXBean proxy or a StandardMBean instance that is an MXBean,
+ * or null if this instance is not for an MXBean.
+ * The default value is null.
+ *
+ * @return the mappings to be used in this proxy or StandardMBean,
+ * or null if this instance is not for an MXBean.
+ */
+ public MXBeanMappingFactory getMXBeanMappingFactory() {
+ return mappingFactory;
+ }
+
+ /**
+ * Set the {@link #getMXBeanMappingFactory() MXBeanMappingFactory} to
+ * the given value. The value should be null if this instance is not
+ * for an MXBean. If this instance is for an MXBean, the value should
+ * usually be either a custom mapping factory, or
+ * {@link MXBeanMappingFactory#forInterface
+ * MXBeanMappingFactory.forInterface}{@code (mxbeanInterface)}
+ * which signifies
+ * that the {@linkplain MXBeanMappingFactory#DEFAULT default} mapping
+ * factory should be used unless an {@code @}{@link
+ * javax.management.openmbean.MXBeanMappingFactoryClass
+ * MXBeanMappingFactoryClass} annotation on {@code mxbeanInterface}
+ * specifies otherwise.
+ *
+ * Examples:
+ *
+ * MBeanOptions opts = new MBeanOptions();
+ * opts.setMXBeanMappingFactory(myMappingFactory);
+ * MyMXBean proxy = JMX.newMBeanProxy(
+ * mbeanServerConnection, objectName, MyMXBean.class, opts);
+ *
+ * // ...or...
+ *
+ * MBeanOptions opts = new MBeanOptions();
+ * MXBeanMappingFactory defaultFactoryForMyMXBean =
+ * MXBeanMappingFactory.forInterface(MyMXBean.class);
+ * opts.setMXBeanMappingFactory(defaultFactoryForMyMXBean);
+ * MyMXBean proxy = JMX.newMBeanProxy(
+ * mbeanServerConnection, objectName, MyMXBean.class, opts);
+ *
+ *
+ * @param f the new value. If null, this instance is not for an
+ * MXBean.
+ */
+ public void setMXBeanMappingFactory(MXBeanMappingFactory f) {
+ this.mappingFactory = f;
+ }
+
+ /* To maximise object sharing, classes in this package can replace
+ * a private MBeanOptions with no MXBeanMappingFactory with one
+ * of these shared instances. But they must be EXTREMELY careful
+ * never to give out the shared instances to user code, which could
+ * modify them.
+ */
+ private static final MBeanOptions[] CANONICALS = {
+ new MBeanOptions(), MXBEAN,
+ };
+ // Overridden in local subclasses:
+ MBeanOptions[] canonicals() {
+ return CANONICALS;
+ }
+
+ // This is only used by the logic for canonical instances.
+ // Overridden in local subclasses:
+ boolean same(MBeanOptions opt) {
+ return (opt.mappingFactory == mappingFactory);
+ }
+
+ final MBeanOptions canonical() {
+ for (MBeanOptions opt : canonicals()) {
+ if (opt.getClass() == this.getClass() && same(opt))
+ return opt;
+ }
+ return this;
+ }
+
+ final MBeanOptions uncanonical() {
+ for (MBeanOptions opt : canonicals()) {
+ if (this == opt)
+ return clone();
+ }
+ return this;
+ }
+
+ private Map toMap() {
+ Map map = new TreeMap();
+ try {
+ BeanInfo bi = java.beans.Introspector.getBeanInfo(getClass());
+ PropertyDescriptor[] pds = bi.getPropertyDescriptors();
+ for (PropertyDescriptor pd : pds) {
+ String name = pd.getName();
+ if (name.equals("class"))
+ continue;
+ Method get = pd.getReadMethod();
+ if (get != null)
+ map.put(name, get.invoke(this));
+ }
+ } catch (Exception e) {
+ Throwable t = e;
+ if (t instanceof InvocationTargetException)
+ t = t.getCause();
+ map.put("Exception", t);
+ }
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + toMap();
+ // For example "MBeanOptions{MXBean=true, }".
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one. The
+ * result is true if and only if the other object is also an instance
+ * of MBeanOptions or a subclass, and has the same properties with
+ * the same values.
+ * @return {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (obj == null || obj.getClass() != this.getClass())
+ return false;
+ return toMap().equals(((MBeanOptions) obj).toMap());
+ }
+
+ @Override
+ public int hashCode() {
+ return toMap().hashCode();
+ }
+ }
+
+ /**
+ * Options to apply to an MBean proxy.
+ *
+ * @see #newMBeanProxy
+ */
+ public static class ProxyOptions extends MBeanOptions {
+ private static final long serialVersionUID = 7238804866098386559L;
+
+ private boolean notificationEmitter;
+
+ /**
+ * Construct a {@code ProxyOptions} object where all options have
+ * their default values.
+ */
+ public ProxyOptions() {}
+
+ @Override
+ public ProxyOptions clone() {
+ return (ProxyOptions) super.clone();
+ }
+
+ /**
+ * Defines whether the returned proxy should
+ * implement {@link NotificationEmitter}. The default value is false.
+ *
+ * @return true if this proxy will be a NotificationEmitter.
+ *
+ * @see JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class,
+ * MBeanOptions)
+ */
+ public boolean isNotificationEmitter() {
+ return this.notificationEmitter;
+ }
+
+ /**
+ * Set the {@link #isNotificationEmitter NotificationEmitter} option to
+ * the given value.
+ * @param emitter the new value.
+ */
+ public void setNotificationEmitter(boolean emitter) {
+ this.notificationEmitter = emitter;
+ }
+
+ // Canonical objects for each of (MXBean,!MXBean) x (Emitter,!Emitter)
+ private static final ProxyOptions[] CANONICALS = {
+ new ProxyOptions(), new ProxyOptions(),
+ new ProxyOptions(), new ProxyOptions(),
+ };
+ static {
+ CANONICALS[1].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT);
+ CANONICALS[2].setNotificationEmitter(true);
+ CANONICALS[3].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT);
+ CANONICALS[3].setNotificationEmitter(true);
+ }
+ @Override
+ MBeanOptions[] canonicals() {
+ return CANONICALS;
+ }
+
+ @Override
+ boolean same(MBeanOptions opt) {
+ return (super.same(opt) && opt instanceof ProxyOptions &&
+ ((ProxyOptions) opt).notificationEmitter == notificationEmitter);
+ }
+ }
+
/**
* Make a proxy for a Standard MBean in a local or remote
* MBean Server.
@@ -172,7 +449,7 @@ public class JMX {
*
* This method behaves the same as {@link
* #newMBeanProxy(MBeanServerConnection, ObjectName, Class)}, but
- * additionally, if {@code notificationBroadcaster} is {@code
+ * additionally, if {@code notificationEmitter} is {@code
* true}, then the MBean is assumed to be a {@link
* NotificationBroadcaster} or {@link NotificationEmitter} and the
* returned proxy will implement {@link NotificationEmitter} as
@@ -189,25 +466,21 @@ public class JMX {
* {@code connection} to forward to.
* @param interfaceClass the management interface that the MBean
* exports, which will also be implemented by the returned proxy.
- * @param notificationBroadcaster make the returned proxy
+ * @param notificationEmitter make the returned proxy
* implement {@link NotificationEmitter} by forwarding its methods
* via {@code connection}.
- *
* @param allows the compiler to know that if the {@code
* interfaceClass} parameter is {@code MyMBean.class}, for
* example, then the return type is {@code MyMBean}.
- *
* @return the new proxy instance.
*/
public static T newMBeanProxy(MBeanServerConnection connection,
ObjectName objectName,
Class interfaceClass,
- boolean notificationBroadcaster) {
- return MBeanServerInvocationHandler.newProxyInstance(
- connection,
- objectName,
- interfaceClass,
- notificationBroadcaster);
+ boolean notificationEmitter) {
+ ProxyOptions opts = new ProxyOptions();
+ opts.setNotificationEmitter(notificationEmitter);
+ return newMBeanProxy(connection, objectName, interfaceClass, opts);
}
/**
@@ -314,7 +587,7 @@ public class JMX {
*
* This method behaves the same as {@link
* #newMXBeanProxy(MBeanServerConnection, ObjectName, Class)}, but
- * additionally, if {@code notificationBroadcaster} is {@code
+ * additionally, if {@code notificationEmitter} is {@code
* true}, then the MXBean is assumed to be a {@link
* NotificationBroadcaster} or {@link NotificationEmitter} and the
* returned proxy will implement {@link NotificationEmitter} as
@@ -331,31 +604,105 @@ public class JMX {
* {@code connection} to forward to.
* @param interfaceClass the MXBean interface,
* which will also be implemented by the returned proxy.
- * @param notificationBroadcaster make the returned proxy
+ * @param notificationEmitter make the returned proxy
* implement {@link NotificationEmitter} by forwarding its methods
* via {@code connection}.
- *
* @param allows the compiler to know that if the {@code
* interfaceClass} parameter is {@code MyMXBean.class}, for
* example, then the return type is {@code MyMXBean}.
- *
* @return the new proxy instance.
*/
public static T newMXBeanProxy(MBeanServerConnection connection,
ObjectName objectName,
Class interfaceClass,
- boolean notificationBroadcaster) {
- // Check interface for MXBean compliance
- //
+ boolean notificationEmitter) {
+ ProxyOptions opts = new ProxyOptions();
+ MXBeanMappingFactory f = MXBeanMappingFactory.forInterface(interfaceClass);
+ opts.setMXBeanMappingFactory(f);
+ opts.setNotificationEmitter(notificationEmitter);
+ return newMBeanProxy(connection, objectName, interfaceClass, opts);
+ }
+
+ /**
+ * Make a proxy for a Standard MBean or MXBean in a local or remote MBean
+ * Server that may also support the methods of {@link
+ * NotificationEmitter} and (for an MXBean) that may define custom MXBean
+ * type mappings.
+ *
+ * This method behaves the same as
+ * {@link #newMBeanProxy(MBeanServerConnection, ObjectName, Class)} or
+ * {@link #newMXBeanProxy(MBeanServerConnection, ObjectName, Class)},
+ * according as {@code opts.isMXBean()} is respectively false or true; but
+ * with the following changes based on {@code opts}.
+ *
+ *
+ * If {@code opts.isNotificationEmitter()} is {@code
+ * true}, then the MBean is assumed to be a {@link
+ * NotificationBroadcaster} or {@link NotificationEmitter} and the
+ * returned proxy will implement {@link NotificationEmitter} as
+ * well as {@code interfaceClass}. A call to {@link
+ * NotificationBroadcaster#addNotificationListener} on the proxy
+ * will result in a call to {@link
+ * MBeanServerConnection#addNotificationListener(ObjectName,
+ * NotificationListener, NotificationFilter, Object)}, and
+ * likewise for the other methods of {@link
+ * NotificationBroadcaster} and {@link NotificationEmitter}.
+ *
+ * If {@code opts.getMXBeanMappingFactory()} is not null,
+ * then the mappings it defines will be applied to convert between
+ * arbitrary Java types and Open Types.
+ *
+ *
+ * @param connection the MBean server to forward to.
+ * @param objectName the name of the MBean within
+ * {@code connection} to forward to.
+ * @param interfaceClass the Standard MBean or MXBean interface,
+ * which will also be implemented by the returned proxy.
+ * @param opts the options to apply for this proxy. Can be null,
+ * in which case default options are applied.
+ * @param allows the compiler to know that if the {@code
+ * interfaceClass} parameter is {@code MyMXBean.class}, for
+ * example, then the return type is {@code MyMXBean}.
+ * @return the new proxy instance.
+ *
+ * @throws IllegalArgumentException if {@code interfaceClass} is not a
+ * valid MXBean interface.
+ */
+ public static T newMBeanProxy(MBeanServerConnection connection,
+ ObjectName objectName,
+ Class interfaceClass,
+ MBeanOptions opts) {
try {
- Introspector.testComplianceMXBeanInterface(interfaceClass);
+ return newMBeanProxy2(connection, objectName, interfaceClass, opts);
} catch (NotCompliantMBeanException e) {
throw new IllegalArgumentException(e);
}
+ }
+
+ private static T newMBeanProxy2(MBeanServerConnection connection,
+ ObjectName objectName,
+ Class interfaceClass,
+ MBeanOptions opts)
+ throws NotCompliantMBeanException {
+
+ if (opts == null)
+ opts = new MBeanOptions();
+
+ boolean notificationEmitter = opts instanceof ProxyOptions &&
+ ((ProxyOptions) opts).isNotificationEmitter();
+
+ MXBeanMappingFactory mappingFactory = opts.getMXBeanMappingFactory();
+
+ if (mappingFactory != null) {
+ // Check interface for MXBean compliance
+ Introspector.testComplianceMXBeanInterface(interfaceClass,
+ mappingFactory);
+ }
+
InvocationHandler handler = new MBeanServerInvocationHandler(
- connection, objectName, true);
+ connection, objectName, opts);
final Class[] interfaces;
- if (notificationBroadcaster) {
+ if (notificationEmitter) {
interfaces =
new Class>[] {interfaceClass, NotificationEmitter.class};
} else
diff --git a/src/share/classes/javax/management/MBeanServerInvocationHandler.java b/src/share/classes/javax/management/MBeanServerInvocationHandler.java
index fb5d4bd25e048c1a43100551a7620ecbf094fb89..d35c4ae6def64e6abc946b69e93e0b1fb8545c50 100644
--- a/src/share/classes/javax/management/MBeanServerInvocationHandler.java
+++ b/src/share/classes/javax/management/MBeanServerInvocationHandler.java
@@ -33,6 +33,9 @@ import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.WeakHashMap;
+import javax.management.openmbean.MXBeanMappingFactory;
+
+import static javax.management.JMX.MBeanOptions;
/**
* {@link InvocationHandler} that forwards methods in an MBean's
@@ -111,7 +114,7 @@ public class MBeanServerInvocationHandler implements InvocationHandler {
public MBeanServerInvocationHandler(MBeanServerConnection connection,
ObjectName objectName) {
- this(connection, objectName, false);
+ this(connection, objectName, null);
}
/**
@@ -138,6 +141,14 @@ public class MBeanServerInvocationHandler implements InvocationHandler {
public MBeanServerInvocationHandler(MBeanServerConnection connection,
ObjectName objectName,
boolean isMXBean) {
+ this(connection, objectName, isMXBean ? MBeanOptions.MXBEAN : null);
+ }
+
+ public MBeanServerInvocationHandler(MBeanServerConnection connection,
+ ObjectName objectName,
+ MBeanOptions options) {
+ if (options == null)
+ options = new MBeanOptions();
if (connection == null) {
throw new IllegalArgumentException("Null connection");
}
@@ -146,7 +157,7 @@ public class MBeanServerInvocationHandler implements InvocationHandler {
}
this.connection = connection;
this.objectName = objectName;
- this.isMXBean = isMXBean;
+ this.options = options.canonical();
}
/**
@@ -182,7 +193,16 @@ public class MBeanServerInvocationHandler implements InvocationHandler {
* @since 1.6
*/
public boolean isMXBean() {
- return isMXBean;
+ return options.isMXBean();
+ }
+
+ /**
+ *
Return the {@link MBeanOptions} used for this proxy.
+ *
+ * @return the MBeanOptions.
+ */
+ public MBeanOptions getMBeanOptions() {
+ return options.uncanonical();
}
/**
@@ -260,7 +280,7 @@ public class MBeanServerInvocationHandler implements InvocationHandler {
return doLocally(proxy, method, args);
try {
- if (isMXBean) {
+ if (isMXBean()) {
MXBeanProxy p = findMXBeanProxy(methodClass);
return p.invoke(connection, objectName, method, args);
} else {
@@ -326,21 +346,34 @@ public class MBeanServerInvocationHandler implements InvocationHandler {
*/
}
- private static MXBeanProxy findMXBeanProxy(Class> mxbeanInterface) {
+ private MXBeanProxy findMXBeanProxy(Class> mxbeanInterface) {
+ MXBeanMappingFactory mappingFactory = options.getMXBeanMappingFactory();
synchronized (mxbeanProxies) {
- WeakReference proxyRef =
- mxbeanProxies.get(mxbeanInterface);
- MXBeanProxy p = (proxyRef == null) ? null : proxyRef.get();
- if (p == null) {
- p = new MXBeanProxy(mxbeanInterface);
- mxbeanProxies.put(mxbeanInterface,
- new WeakReference(p));
+ ClassToProxy classToProxy = mxbeanProxies.get(mappingFactory);
+ if (classToProxy == null) {
+ classToProxy = new ClassToProxy();
+ mxbeanProxies.put(mappingFactory, classToProxy);
}
+ WeakReference wr = classToProxy.get(mxbeanInterface);
+ MXBeanProxy p;
+ if (wr != null) {
+ p = wr.get();
+ if (p != null)
+ return p;
+ }
+ p = new MXBeanProxy(mxbeanInterface, mappingFactory);
+ classToProxy.put(mxbeanInterface, new WeakReference(p));
return p;
}
}
- private static final WeakHashMap, WeakReference>
- mxbeanProxies = new WeakHashMap, WeakReference>();
+ private static final WeakHashMap
+ mxbeanProxies = newWeakHashMap();
+ private static class ClassToProxy
+ extends WeakHashMap, WeakReference> {}
+
+ private static WeakHashMap newWeakHashMap() {
+ return new WeakHashMap();
+ }
private Object invokeBroadcasterMethod(Object proxy, Method method,
Object[] args) throws Exception {
@@ -453,7 +486,7 @@ public class MBeanServerInvocationHandler implements InvocationHandler {
objectName.equals(handler.objectName) &&
proxy.getClass().equals(args[0].getClass());
} else if (methodName.equals("toString")) {
- return (isMXBean ? "MX" : "M") + "BeanProxy(" +
+ return (isMXBean() ? "MX" : "M") + "BeanProxy(" +
connection + "[" + objectName + "])";
} else if (methodName.equals("hashCode")) {
return objectName.hashCode()+connection.hashCode();
@@ -484,5 +517,5 @@ public class MBeanServerInvocationHandler implements InvocationHandler {
private final MBeanServerConnection connection;
private final ObjectName objectName;
- private final boolean isMXBean;
+ private final MBeanOptions options;
}
diff --git a/src/share/classes/javax/management/MXBean.java b/src/share/classes/javax/management/MXBean.java
index 166ea57a3d416d4fbe6989af185d2480c0dc3dee..d707bd3305859f44e8ecc9593644079b2965dac2 100644
--- a/src/share/classes/javax/management/MXBean.java
+++ b/src/share/classes/javax/management/MXBean.java
@@ -44,6 +44,10 @@ import javax.management.openmbean.CompositeDataInvocationHandler;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeDataView;
import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.MXBeanMapping;
+import javax.management.openmbean.MXBeanMappingClass;
+import javax.management.openmbean.MXBeanMappingFactory;
+import javax.management.openmbean.MXBeanMappingFactoryClass;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenMBeanInfo;
import javax.management.openmbean.OpenType;
@@ -78,7 +82,7 @@ import javax.management.openmbean.TabularType;
public interface MisleadingMXBean {}
-
+ MXBean specification
The MXBean concept provides a simple way to code an MBean
that only references a predefined set of types, the ones defined
@@ -314,7 +318,7 @@ public class MemoryPool
-
+ Definition of an MXBean
An MXBean is a kind of MBean. An MXBean object can be
registered directly in the MBean Server, or it can be used as an
@@ -367,7 +371,7 @@ public class MemoryPool
above rules will produce an exception.
-
+ Naming conventions
The same naming conventions are applied to the methods in an
MXBean as in a Standard MBean:
@@ -413,7 +417,7 @@ public class MemoryPool
read-only or write-only respectively.
-
+ Type mapping rules
An MXBean is a kind of Open MBean, as defined by the {@link
javax.management.openmbean} package. This means that the types of
@@ -475,7 +479,11 @@ public class MemoryPool
from type opendata(J) to type J , a null value is
mapped to a null value.
- The following table summarizes the type mapping rules.
+ In addition to the default type mapping rules, you can specify
+ custom type mappings, as described below .
+
+ The following table summarizes the default type mapping rules.
@@ -658,7 +666,7 @@ TabularType tabularType =
TabularData} that serializes as {@code TabularDataSupport}.
-
+ Mappings for MXBean interfaces
An MXBean interface, or a type referenced within an MXBean
interface, can reference another MXBean interface, J .
@@ -747,7 +755,7 @@ public interface ModuleMXBean {
general, notably because it does not work well for MBeans that are
{@link NotificationBroadcaster}s.
-
+ Mappings for other types
Given a Java class or interface J that does not match the other
rules in the table above, the MXBean framework will attempt to map
@@ -1035,6 +1043,76 @@ public interface Node {
}
+
Alternatively, you can define a custom mapping for your recursive
+ type; see the next section.
+
+ Custom MXBean type mappings
+
+ You can augment or replace the default type mappings described
+ above with custom mappings. An example appears in the
+ documentation for {@link MXBeanMapping}.
+
+ If an MXBean uses custom mappings, then an MXBean proxy for
+ that MXBean must use the same mappings for correct behavior.
+ This requires more careful synchronization between client and
+ server than is necessary with the default mappings. For example
+ it typically requires the client to have the same implementation
+ of any {@link MXBeanMapping} subclasses as the server. For this
+ reason, custom mappings should be avoided if possible.
+
+ Every MXBean has an associated {@link MXBeanMappingFactory}.
+ Call this f . Then every type that appears
+ in that MXBean has an associated {@link MXBeanMapping}
+ determined by f . If the type is
+ J , say, then the mapping is {@link
+ MXBeanMappingFactory#mappingForType
+ f .mappingForType}(J ,
+ f ).
+
+ The {@code MXBeanMappingFactory} f for an
+ MXBean is determined as follows.
+
+
+ If an {@link JMX.MBeanOptions} argument is supplied to
+ the {@link StandardMBean} constructor that makes an MXBean,
+ or to the {@link JMX#newMXBeanProxy JMX.newMXBeanProxy}
+ method, and the {@code MBeanOptions} object defines a non-null
+ {@code MXBeanMappingFactory}, then that is the value of
+ f .
+
+ Otherwise, if the MXBean interface has an {@link
+ MXBeanMappingFactoryClass} annotation, then that annotation
+ must identify a subclass of {@code MXBeanMappingFactory}
+ with a no-argument constructor. Then
+ f is the result of calling this
+ constructor. If the class does not have a no-argument
+ constructor, or if calling the constructor produces an
+ exception, then the MXBean is invalid and an attempt to
+ register it in the MBean Server will produce a {@link
+ NotCompliantMBeanException}.
+
+ This annotation is not inherited from any parent
+ interfaces. If an MXBean interface has this annotation,
+ then usually any MXBean subinterfaces must repeat the same
+ annotation for correct behavior.
+
+ Otherwise, if the package in which the MXBean interface
+ appears has an {@code MXBeanMappingFactoryClass} annotation,
+ then f is determined as if that
+ annotation appeared on the MXBean interface.
+
+ Otherwise, f is the default mapping
+ factory, {@link MXBeanMappingFactory#DEFAULT}.
+
+
+ The default mapping factory recognizes the {@link
+ MXBeanMappingClass} annotation on a class or interface. If
+ J is a class or interface that has such an
+ annotation, then the {@code MXBeanMapping} for
+ J produced by the default mapping factory
+ will be determined by the value of the annotation as described
+ in its {@linkplain MXBeanMappingClass documentation}.
+
MBeanInfo contents for an MXBean
An MXBean is a type of Open MBean. However, for compatibility
@@ -1091,7 +1169,7 @@ public interface Node {
{@code mxbean} whose value is the string "{@code true}".
-
+ Type Names
Sometimes the unmapped type T of a method parameter or
return value in an MXBean must be represented as a string. If
@@ -1163,6 +1241,8 @@ public interface Node {
appropriate), or C is true of e .{@link
Throwable#getCause() getCause()}".
+ @see MXBeanMapping
+
@since 1.6
*/
diff --git a/src/share/classes/javax/management/StandardMBean.java b/src/share/classes/javax/management/StandardMBean.java
index fa722cb8d34244935e16253d873cc906c85b19a2..828a2a87bb11be256ef8c79cf93df4d9fa27289e 100644
--- a/src/share/classes/javax/management/StandardMBean.java
+++ b/src/share/classes/javax/management/StandardMBean.java
@@ -25,22 +25,19 @@
package javax.management;
-import static com.sun.jmx.defaults.JmxProperties.MISC_LOGGER;
import com.sun.jmx.mbeanserver.DescriptorCache;
import com.sun.jmx.mbeanserver.Introspector;
import com.sun.jmx.mbeanserver.MBeanSupport;
import com.sun.jmx.mbeanserver.MXBeanSupport;
import com.sun.jmx.mbeanserver.StandardMBeanSupport;
import com.sun.jmx.mbeanserver.Util;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
+import javax.management.openmbean.MXBeanMappingFactory;
import javax.management.openmbean.OpenMBeanAttributeInfo;
import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
import javax.management.openmbean.OpenMBeanConstructorInfo;
@@ -50,6 +47,9 @@ import javax.management.openmbean.OpenMBeanOperationInfoSupport;
import javax.management.openmbean.OpenMBeanParameterInfo;
import javax.management.openmbean.OpenMBeanParameterInfoSupport;
+import static com.sun.jmx.defaults.JmxProperties.MISC_LOGGER;
+import static javax.management.JMX.MBeanOptions;
+
/**
* An MBean whose management interface is determined by reflection
* on a Java interface.
@@ -140,6 +140,11 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
**/
private volatile MBeanInfo cachedMBeanInfo;
+ /**
+ * The MBeanOptions for this StandardMBean.
+ **/
+ private MBeanOptions options;
+
/**
* Make a DynamicMBean out of implementation , using the
* specified mbeanInterface class.
@@ -155,12 +160,14 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
* implementation is allowed. If null implementation is allowed,
* and a null implementation is passed, then the implementation
* is assumed to be this .
+ * @param options MBeanOptions to apply to this instance.
* @exception IllegalArgumentException if the given
* implementation is null, and null is not allowed.
**/
+ @SuppressWarnings("unchecked") // cast to T
private void construct(T implementation, Class mbeanInterface,
boolean nullImplementationAllowed,
- boolean isMXBean)
+ MBeanOptions options)
throws NotCompliantMBeanException {
if (implementation == null) {
// Have to use (T)this rather than mbeanInterface.cast(this)
@@ -169,20 +176,23 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
implementation = Util.cast(this);
else throw new IllegalArgumentException("implementation is null");
}
- if (isMXBean) {
- if (mbeanInterface == null) {
- mbeanInterface = Util.cast(Introspector.getMXBeanInterface(
- implementation.getClass()));
- }
- this.mbean = new MXBeanSupport(implementation, mbeanInterface);
+ if (options == null)
+ options = new MBeanOptions();
+ MXBeanMappingFactory mappingFactory = options.getMXBeanMappingFactory();
+ boolean mx = (mappingFactory != null);
+ if (mbeanInterface == null) {
+ mbeanInterface = Util.cast(Introspector.getStandardOrMXBeanInterface(
+ implementation.getClass(), mx));
+ }
+ if (mx) {
+ this.mbean =
+ new MXBeanSupport(implementation, mbeanInterface,
+ mappingFactory);
} else {
- if (mbeanInterface == null) {
- mbeanInterface = Util.cast(Introspector.getStandardMBeanInterface(
- implementation.getClass()));
- }
this.mbean =
new StandardMBeanSupport(implementation, mbeanInterface);
}
+ this.options = options.canonical();
}
/**
@@ -211,14 +221,14 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
**/
public StandardMBean(T implementation, Class mbeanInterface)
throws NotCompliantMBeanException {
- construct(implementation, mbeanInterface, false, false);
+ construct(implementation, mbeanInterface, false, null);
}
/**
* Make a DynamicMBean out of this , using the specified
* mbeanInterface class.
*
- * Call {@link #StandardMBean(java.lang.Object, java.lang.Class)
+ *
Calls {@link #StandardMBean(java.lang.Object, java.lang.Class)
* this(this,mbeanInterface)}.
* This constructor is reserved to subclasses.
*
@@ -231,13 +241,14 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
**/
protected StandardMBean(Class> mbeanInterface)
throws NotCompliantMBeanException {
- construct(null, mbeanInterface, true, false);
+ construct(null, mbeanInterface, true, null);
}
/**
* Make a DynamicMBean out of the object
* implementation , using the specified
- * mbeanInterface class. This constructor can be used
+ * mbeanInterface class, and choosing whether the
+ * resultant MBean is an MXBean. This constructor can be used
* to make either Standard MBeans or MXBeans. Unlike the
* constructor {@link #StandardMBean(Object, Class)}, it
* does not throw NotCompliantMBeanException.
@@ -267,7 +278,17 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
public StandardMBean(T implementation, Class mbeanInterface,
boolean isMXBean) {
try {
- construct(implementation, mbeanInterface, false, isMXBean);
+ MBeanOptions opts = new MBeanOptions();
+ if (mbeanInterface == null) {
+ mbeanInterface = Util.cast(Introspector.getStandardOrMXBeanInterface(
+ implementation.getClass(), isMXBean));
+ }
+ if (isMXBean) {
+ MXBeanMappingFactory f = MXBeanMappingFactory.forInterface(
+ mbeanInterface);
+ opts.setMXBeanMappingFactory(f);
+ }
+ construct(implementation, mbeanInterface, false, opts);
} catch (NotCompliantMBeanException e) {
throw new IllegalArgumentException(e);
}
@@ -275,12 +296,13 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
/**
* Make a DynamicMBean out of this , using the specified
- * mbeanInterface class. This constructor can be used
+ * mbeanInterface class, and choosing whether the resulting
+ * MBean is an MXBean. This constructor can be used
* to make either Standard MBeans or MXBeans. Unlike the
* constructor {@link #StandardMBean(Object, Class)}, it
* does not throw NotCompliantMBeanException.
*
- * Call {@link #StandardMBean(java.lang.Object, java.lang.Class, boolean)
+ *
Calls {@link #StandardMBean(java.lang.Object, java.lang.Class, boolean)
* this(this, mbeanInterface, isMXBean)}.
* This constructor is reserved to subclasses.
*
@@ -297,7 +319,77 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
**/
protected StandardMBean(Class> mbeanInterface, boolean isMXBean) {
try {
- construct(null, mbeanInterface, true, isMXBean);
+ MBeanOptions opts = new MBeanOptions();
+ if (mbeanInterface == null) {
+ mbeanInterface = Introspector.getStandardOrMXBeanInterface(
+ getClass(), isMXBean);
+ }
+ if (isMXBean) {
+ MXBeanMappingFactory f = MXBeanMappingFactory.forInterface(
+ mbeanInterface);
+ opts.setMXBeanMappingFactory(f);
+ }
+ construct(null, mbeanInterface, true, opts);
+ } catch (NotCompliantMBeanException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Make a DynamicMBean out of the object
+ * implementation , using the specified
+ * mbeanInterface class and the specified options.
+ *
+ * @param implementation The implementation of this MBean.
+ * @param mbeanInterface The Management Interface exported by this
+ * MBean's implementation. If null, then this
+ * object will use standard JMX design pattern to determine
+ * the management interface associated with the given
+ * implementation.
+ * @param options MBeanOptions that control the operation of the resulting
+ * MBean, as documented in the {@link MBeanOptions} class.
+ * @param Allows the compiler to check
+ * that {@code implementation} does indeed implement the class
+ * described by {@code mbeanInterface}. The compiler can only
+ * check this if {@code mbeanInterface} is a class literal such
+ * as {@code MyMBean.class}.
+ *
+ * @exception IllegalArgumentException if the given
+ * implementation is null, or if the mbeanInterface
+ * does not follow JMX design patterns for Management Interfaces, or
+ * if the given implementation does not implement the
+ * specified interface.
+ **/
+ public StandardMBean(T implementation,
+ Class mbeanInterface,
+ MBeanOptions options) {
+ try {
+ construct(implementation, mbeanInterface, false, options);
+ } catch (NotCompliantMBeanException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Make a DynamicMBean out of this , using the specified
+ * mbeanInterface class and the specified options.
+ *
+ * Calls {@link #StandardMBean(Object, Class, JMX.MBeanOptions)
+ * this(this,mbeanInterface,options)}.
+ * This constructor is reserved to subclasses.
+ *
+ * @param mbeanInterface The Management Interface exported by this
+ * MBean.
+ * @param options MBeanOptions that control the operation of the resulting
+ * MBean, as documented in the {@link MBeanOptions} class.
+ *
+ * @exception IllegalArgumentException if the mbeanInterface
+ * does not follow JMX design patterns for Management Interfaces, or
+ * if this does not implement the specified interface.
+ **/
+ protected StandardMBean(Class> mbeanInterface, MBeanOptions options) {
+ try {
+ construct(null, mbeanInterface, true, options);
} catch (NotCompliantMBeanException e) {
throw new IllegalArgumentException(e);
}
@@ -326,13 +418,19 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
if (implementation == null)
throw new IllegalArgumentException("implementation is null");
+ setImplementation2(implementation);
+ }
- if (isMXBean()) {
+ private void setImplementation2(T implementation)
+ throws NotCompliantMBeanException {
+ Class super T> intf = Util.cast(getMBeanInterface());
+
+ if (this.mbean.isMXBean()) {
this.mbean = new MXBeanSupport(implementation,
- Util.>cast(getMBeanInterface()));
+ intf,
+ options.getMXBeanMappingFactory());
} else {
- this.mbean = new StandardMBeanSupport(implementation,
- Util.>cast(getMBeanInterface()));
+ this.mbean = new StandardMBeanSupport(implementation, intf);
}
}
@@ -362,6 +460,19 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
return mbean.getResource().getClass();
}
+ /**
+ * Return the MBeanOptions that were specified or implied for this StandardMBean
+ * instance. If an MBeanOptions object was supplied when this StandardMBean
+ * instance was constructed, and if that object has not been modified in the
+ * meantime, then the returned object will be equal to that object, although
+ * it might not be the same object.
+ * @return The MBeanOptions that were specified or implied for this StandardMBean
+ * instance.
+ */
+ public MBeanOptions getOptions() {
+ return options.uncanonical();
+ }
+
// ------------------------------------------------------------------
// From the DynamicMBean interface.
// ------------------------------------------------------------------
@@ -726,7 +837,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
* @return the MBeanNotificationInfo[] for the new MBeanInfo.
**/
MBeanNotificationInfo[] getNotifications(MBeanInfo info) {
- return null;
+ return info.getNotifications();
}
/**
@@ -1234,5 +1345,4 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
return true;
}
}
-
}
diff --git a/src/share/classes/javax/management/openmbean/CompositeDataInvocationHandler.java b/src/share/classes/javax/management/openmbean/CompositeDataInvocationHandler.java
index e0d95dfdccd5ed65dcabb2aa31d20d91885f2228..c09c39009a38292d34becf14e54a369e8a8a18c8 100644
--- a/src/share/classes/javax/management/openmbean/CompositeDataInvocationHandler.java
+++ b/src/share/classes/javax/management/openmbean/CompositeDataInvocationHandler.java
@@ -26,7 +26,7 @@
package javax.management.openmbean;
import com.sun.jmx.mbeanserver.MXBeanLookup;
-import com.sun.jmx.mbeanserver.OpenConverter;
+import com.sun.jmx.mbeanserver.DefaultMXBeanMappingFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@@ -115,7 +115,12 @@ public class CompositeDataInvocationHandler implements InvocationHandler {
is null.
*/
public CompositeDataInvocationHandler(CompositeData compositeData) {
- this(compositeData, null);
+ this(compositeData, MXBeanMappingFactory.DEFAULT);
+ }
+
+ public CompositeDataInvocationHandler(CompositeData compositeData,
+ MXBeanMappingFactory mappingFactory) {
+ this(compositeData, mappingFactory, null);
}
/**
@@ -134,11 +139,13 @@ public class CompositeDataInvocationHandler implements InvocationHandler {
is null.
*/
CompositeDataInvocationHandler(CompositeData compositeData,
+ MXBeanMappingFactory mappingFactory,
MXBeanLookup lookup) {
if (compositeData == null)
throw new IllegalArgumentException("compositeData");
this.compositeData = compositeData;
this.lookup = lookup;
+ this.mappingFactory = mappingFactory;
}
/**
@@ -176,7 +183,7 @@ public class CompositeDataInvocationHandler implements InvocationHandler {
}
}
- String propertyName = OpenConverter.propertyName(method);
+ String propertyName = DefaultMXBeanMappingFactory.propertyName(method);
if (propertyName == null) {
throw new IllegalArgumentException("Method is not getter: " +
method.getName());
@@ -185,7 +192,7 @@ public class CompositeDataInvocationHandler implements InvocationHandler {
if (compositeData.containsKey(propertyName))
openValue = compositeData.get(propertyName);
else {
- String decap = OpenConverter.decapitalize(propertyName);
+ String decap = DefaultMXBeanMappingFactory.decapitalize(propertyName);
if (compositeData.containsKey(decap))
openValue = compositeData.get(decap);
else {
@@ -196,9 +203,10 @@ public class CompositeDataInvocationHandler implements InvocationHandler {
throw new IllegalArgumentException(msg);
}
}
- OpenConverter converter =
- OpenConverter.toConverter(method.getGenericReturnType());
- return converter.fromOpenValue(lookup, openValue);
+ MXBeanMapping mapping =
+ mappingFactory.mappingForType(method.getGenericReturnType(),
+ MXBeanMappingFactory.DEFAULT);
+ return mapping.fromOpenValue(openValue);
}
/* This method is called when equals(Object) is
@@ -242,4 +250,5 @@ public class CompositeDataInvocationHandler implements InvocationHandler {
private final CompositeData compositeData;
private final MXBeanLookup lookup;
+ private final MXBeanMappingFactory mappingFactory;
}
diff --git a/src/share/classes/javax/management/openmbean/CompositeType.java b/src/share/classes/javax/management/openmbean/CompositeType.java
index 25f7b1dbb20a5216b45869e7ac7e1cae7c809e99..dae46ba29fdb4e47a2c67e8680e8066f389475a1 100644
--- a/src/share/classes/javax/management/openmbean/CompositeType.java
+++ b/src/share/classes/javax/management/openmbean/CompositeType.java
@@ -159,8 +159,8 @@ public class CompositeType extends OpenType {
}
private static void checkForNullElement(Object[] arg, String argName) {
- if ( (arg == null) || (arg.length == 0) ) {
- throw new IllegalArgumentException("Argument "+ argName +"[] cannot be null or empty.");
+ if (arg == null) {
+ throw new IllegalArgumentException("Argument "+ argName +"[] cannot be null.");
}
for (int i=0; iA custom mapping between Java types and Open types for use in MXBeans.
+ * To define such a mapping, subclass this class and define at least the
+ * {@link #fromOpenValue fromOpenValue} and {@link #toOpenValue toOpenValue}
+ * methods, and optionally the {@link #checkReconstructible} method.
+ * Then either use an {@link MXBeanMappingClass} annotation on your custom
+ * Java types, or include this MXBeanMapping in an
+ * {@link MXBeanMappingFactory}.
+ *
+ * For example, suppose we have a class {@code MyLinkedList}, which looks
+ * like this:
+ *
+ *
+ * public class MyLinkedList {
+ * public MyLinkedList(String name, MyLinkedList next) {...}
+ * public String getName() {...}
+ * public MyLinkedList getNext() {...}
+ * }
+ *
+ *
+ * This is not a valid type for MXBeans, because it contains a
+ * self-referential property "next" defined by the {@code getNext()}
+ * method. MXBeans do not support recursive types. So we would like
+ * to specify a mapping for {@code MyLinkedList} explicitly. When an
+ * MXBean interface contains {@code MyLinkedList}, that will be mapped
+ * into a {@code String[]}, which is a valid Open Type.
+ *
+ * To define this mapping, we first subclass {@code MXBeanMapping}:
+ *
+ *
+ * public class MyLinkedListMapping extends MXBeanMapping {
+ * public MyLinkedListMapping(Type type) throws OpenDataException {
+ * super(MyLinkedList.class, ArrayType.getArrayType(SimpleType.STRING));
+ * if (type != MyLinkedList.class)
+ * throw new OpenDataException("Mapping only valid for MyLinkedList");
+ * }
+ *
+ * {@literal @Override}
+ * public Object fromOpenValue(Object openValue) throws InvalidObjectException {
+ * String[] array = (String[]) openValue;
+ * MyLinkedList list = null;
+ * for (int i = array.length - 1; i >= 0; i--)
+ * list = new MyLinkedList(array[i], list);
+ * return list;
+ * }
+ *
+ * {@literal @Override}
+ * public Object toOpenValue(Object javaValue) throws OpenDataException {
+ * ArrayList<String> array = new ArrayList<String>();
+ * for (MyLinkedList list = (MyLinkedList) javaValue; list != null;
+ * list = list.getNext())
+ * array.add(list.getName());
+ * return array.toArray(new String[0]);
+ * }
+ * }
+ *
+ *
+ * The call to the superclass constructor specifies what the
+ * original Java type is ({@code MyLinkedList.class}) and what Open
+ * Type it is mapped to ({@code
+ * ArrayType.getArrayType(SimpleType.STRING)}). The {@code
+ * fromOpenValue} method says how we go from the Open Type ({@code
+ * String[]}) to the Java type ({@code MyLinkedList}), and the {@code
+ * toOpenValue} method says how we go from the Java type to the Open
+ * Type.
+ *
+ * With this mapping defined, we can annotate the {@code MyLinkedList}
+ * class appropriately:
+ *
+ *
+ * {@literal @MXBeanMappingClass}(MyLinkedListMapping.class)
+ * public class MyLinkedList {...}
+ *
+ *
+ * Now we can use {@code MyLinkedList} in an MXBean interface and it
+ * will work.
+ *
+ * If we are unable to modify the {@code MyLinkedList} class,
+ * we can define an {@link MXBeanMappingFactory}. See the documentation
+ * of that class for further details.
+ */
+public abstract class MXBeanMapping {
+ private final Type javaType;
+ private final OpenType> openType;
+ private final Class> openClass;
+
+ /**
+ * Construct a mapping between the given Java type and the given
+ * Open Type.
+ *
+ * @param javaType the Java type (for example, {@code MyLinkedList}).
+ * @param openType the Open Type (for example, {@code
+ * ArrayType.getArrayType(SimpleType.STRING)})
+ *
+ * @throws NullPointerException if either argument is null.
+ */
+ protected MXBeanMapping(Type javaType, OpenType> openType) {
+ if (javaType == null || openType == null)
+ throw new NullPointerException("Null argument");
+ this.javaType = javaType;
+ this.openType = openType;
+ this.openClass = makeOpenClass(javaType, openType);
+ }
+
+ /**
+ * The Java type that was supplied to the constructor.
+ * @return the Java type that was supplied to the constructor.
+ */
+ public final Type getJavaType() {
+ return javaType;
+ }
+
+ /**
+ * The Open Type that was supplied to the constructor.
+ * @return the Open Type that was supplied to the constructor.
+ */
+ public final OpenType> getOpenType() {
+ return openType;
+ }
+
+ /**
+ * The Java class that corresponds to instances of the
+ * {@linkplain #getOpenType() Open Type} for this mapping.
+ * @return the Java class that corresponds to instances of the
+ * Open Type for this mapping.
+ * @see OpenType#getClassName
+ */
+ public final Class> getOpenClass() {
+ return openClass;
+ }
+
+ private static Class> makeOpenClass(Type javaType, OpenType> openType) {
+ if (javaType instanceof Class> && ((Class>) javaType).isPrimitive())
+ return (Class>) javaType;
+ try {
+ String className = OpenType.validClassName(openType.getClassName());
+ return Class.forName(className, false, null);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e); // should not happen
+ } catch (OpenDataException e) {
+ throw new IllegalArgumentException("Bad OpenType: " + openType, e);
+ }
+ }
+
+ /**
+ * Convert an instance of the Open Type into the Java type.
+ * @param openValue the value to be converted.
+ * @return the converted value.
+ * @throws InvalidObjectException if the value cannot be converted.
+ */
+ public abstract Object fromOpenValue(Object openValue)
+ throws InvalidObjectException;
+
+ /**
+ *
Convert an instance of the Java type into the Open Type.
+ * @param javaValue the value to be converted.
+ * @return the converted value.
+ * @throws OpenDataException if the value cannot be converted.
+ */
+ public abstract Object toOpenValue(Object javaValue)
+ throws OpenDataException;
+
+
+ /**
+ *
Throw an appropriate InvalidObjectException if we will not
+ * be able to convert back from the open data to the original Java
+ * object. The {@link #fromOpenValue fromOpenValue} throws an
+ * exception if a given open data value cannot be converted. This
+ * method throws an exception if no open data values can
+ * be converted. The default implementation of this method never
+ * throws an exception. Subclasses can override it as
+ * appropriate.
+ * @throws InvalidObjectException if {@code fromOpenValue} will throw
+ * an exception no matter what its argument is.
+ */
+ public void checkReconstructible() throws InvalidObjectException {}
+}
diff --git a/src/share/classes/javax/management/openmbean/MXBeanMappingClass.java b/src/share/classes/javax/management/openmbean/MXBeanMappingClass.java
new file mode 100644
index 0000000000000000000000000000000000000000..3337d77d02b09e7fe92661c559856c549e005a45
--- /dev/null
+++ b/src/share/classes/javax/management/openmbean/MXBeanMappingClass.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.management.openmbean;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.management.NotCompliantMBeanException;
+
+/**
+ * Specifies the MXBean mapping to be used for this Java type.
+ * @see MXBeanMapping
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented @Inherited
+public @interface MXBeanMappingClass {
+ /**
+ * The {@link MXBeanMapping} class to be used to map the
+ * annotated type. This class must have a public constructor with
+ * a single argument of type {@link java.lang.reflect.Type}. The
+ * constructor will be called with the annotated type as an
+ * argument. See the {@code MXBeanMapping} documentation
+ * for an example.
+ *
+ * If the {@code MXBeanMapping} cannot in fact handle that
+ * type, the constructor should throw an {@link
+ * OpenDataException}. If the constructor throws this or any other
+ * exception then an MXBean in which the annotated type appears is
+ * invalid, and registering it in the MBean Server will produce a
+ * {@link NotCompliantMBeanException}.
+ */
+ public Class extends MXBeanMapping> value();
+}
diff --git a/src/share/classes/javax/management/openmbean/MXBeanMappingFactory.java b/src/share/classes/javax/management/openmbean/MXBeanMappingFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..69dc6b1030362c248eb9647e5802edd7e3b318b4
--- /dev/null
+++ b/src/share/classes/javax/management/openmbean/MXBeanMappingFactory.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.management.openmbean;
+
+import com.sun.jmx.mbeanserver.DefaultMXBeanMappingFactory;
+import java.lang.reflect.Type;
+
+/**
+ *
Defines how types are mapped for a given MXBean or set of MXBeans.
+ * An {@code MXBeanMappingFactory} can be specified either through the
+ * {@link MXBeanMappingFactoryClass} annotation, or through the
+ * {@link javax.management.JMX.MBeanOptions JMX.MBeanOptions} argument to a
+ * {@link javax.management.StandardMBean StandardMBean} constructor or MXBean
+ * proxy.
+ *
+ * An {@code MXBeanMappingFactory} must return an {@code MXBeanMapping}
+ * for any Java type that appears in the MXBeans that the factory is being
+ * used for. Usually it does that by handling any custom types, and
+ * forwarding everything else to the {@linkplain #DEFAULT default mapping
+ * factory}.
+ *
+ * Consider the {@code MyLinkedList} example from the {@link MXBeanMapping}
+ * documentation. If we are unable to change the {@code MyLinkedList} class
+ * to add an {@link MXBeanMappingClass} annotation, we could achieve the same
+ * effect by defining {@code MyLinkedListMappingFactory} as follows:
+ *
+ *
+ * public class MyLinkedListMappingFactory implements MXBeanMappingFactory {
+ * public MyLinkedListMappingFactory() {}
+ *
+ * public MXBeanMapping mappingForType(Type t, MXBeanMappingFactory f)
+ * throws OpenDataException {
+ * if (t == MyLinkedList.class)
+ * return new MyLinkedListMapping(t);
+ * else
+ * return MXBeanMappingFactory.DEFAULT.mappingForType(t, f);
+ * }
+ * }
+ *
+ *
+ * The mapping factory handles only the {@code MyLinkedList} class.
+ * Every other type is forwarded to the default mapping factory.
+ * This includes types such as {@code MyLinkedList[]} and
+ * {@code List}; the default mapping factory will recursively
+ * invoke {@code MyLinkedListMappingFactory} to map the contained
+ * {@code MyLinkedList} type.
+ *
+ * Once we have defined {@code MyLinkedListMappingFactory}, we can use
+ * it in an MXBean interface like this:
+ *
+ *
+ * {@literal @MXBeanMappingFactoryClass}(MyLinkedListMappingFactory.class)
+ * public interface SomethingMXBean {
+ * public MyLinkedList getSomething();
+ * }
+ *
+ *
+ * Alternatively we can annotate the package that {@code SomethingMXBean}
+ * appears in, or we can supply the factory to a {@link
+ * javax.management.StandardMBean StandardMBean} constructor or MXBean
+ * proxy.
+ */
+public abstract class MXBeanMappingFactory {
+ /**
+ * Construct an instance of this class.
+ */
+ protected MXBeanMappingFactory() {}
+
+ /**
+ * Mapping factory that applies the default rules for MXBean
+ * mappings, as described in the MXBean specification .
+ */
+ public static final MXBeanMappingFactory DEFAULT =
+ new DefaultMXBeanMappingFactory();
+
+ /**
+ * Determine the appropriate MXBeanMappingFactory to use for the given
+ * MXBean interface, based on its annotations. If the interface has an
+ * {@link MXBeanMappingFactoryClass @MXBeanMappingFactoryClass} annotation,
+ * that is used to determine the MXBeanMappingFactory. Otherwise, if the
+ * package containing the interface has such an annotation, that is used.
+ * Otherwise the MXBeanMappingFactory is the {@linkplain #DEFAULT default}
+ * one.
+ *
+ * @param intf the MXBean interface for which to determine the
+ * MXBeanMappingFactory.
+ *
+ * @return the MXBeanMappingFactory for the given MXBean interface.
+ *
+ * @throws IllegalArgumentException if {@code intf} is null, or if an
+ * exception occurs while trying constructing an MXBeanMappingFactory
+ * based on an annotation. In the second case, the exception will appear
+ * in the {@linkplain Throwable#getCause() cause chain} of the
+ * {@code IllegalArgumentException}.
+ */
+ public static MXBeanMappingFactory forInterface(Class> intf) {
+ if (intf == null)
+ throw new IllegalArgumentException("Null interface");
+ MXBeanMappingFactoryClass annot =
+ intf.getAnnotation(MXBeanMappingFactoryClass.class);
+ if (annot == null) {
+ Package p = intf.getPackage();
+ if (p != null)
+ annot = p.getAnnotation(MXBeanMappingFactoryClass.class);
+ }
+ if (annot == null)
+ return MXBeanMappingFactory.DEFAULT;
+ Class extends MXBeanMappingFactory> factoryClass = annot.value();
+ try {
+ return annot.value().newInstance();
+ } catch (Exception e) {
+ throw new IllegalArgumentException(
+ "Could not instantiate MXBeanMappingFactory " +
+ factoryClass.getName() +
+ " from @MXBeanMappingFactoryClass", e);
+ }
+ }
+
+ /**
+ * Return the mapping for the given Java type. Typically, a
+ * mapping factory will return mappings for types it handles, and
+ * forward other types to another mapping factory, most often
+ * the {@linkplain #DEFAULT default one}.
+ * @param t the Java type to be mapped.
+ * @param f the original mapping factory that was consulted to do
+ * the mapping. A mapping factory should pass this parameter intact
+ * if it forwards a type to another mapping factory. In the example,
+ * this is how {@code MyLinkedListMappingFactory} works for types
+ * like {@code MyLinkedList[]} and {@code List}.
+ * @return the mapping for the given type.
+ * @throws OpenDataException if this type cannot be mapped. This
+ * exception is appropriate if the factory is supposed to handle
+ * all types of this sort (for example, all linked lists), but
+ * cannot handle this particular type.
+ */
+ public abstract MXBeanMapping mappingForType(Type t, MXBeanMappingFactory f)
+ throws OpenDataException;
+}
diff --git a/src/share/classes/javax/management/openmbean/MXBeanMappingFactoryClass.java b/src/share/classes/javax/management/openmbean/MXBeanMappingFactoryClass.java
new file mode 100644
index 0000000000000000000000000000000000000000..b852ee7a0d3cb00ef8a29a6c6eda0ac8de626259
--- /dev/null
+++ b/src/share/classes/javax/management/openmbean/MXBeanMappingFactoryClass.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.management.openmbean;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies the MXBean mapping factory to be used for Java types
+ * in an MXBean interface, or in all MXBean interfaces in a package.
+ *
+ * Applying a mapping factory to all Java types in an MXBean interface
+ * looks like this:
+ *
+ *
+ * {@literal @MXBeanMappingFactoryClass}(MyLinkedListMappingFactory.class)
+ * public interface SomethingMXBean {
+ * public MyLinkedList getSomething();
+ * }
+ *
+ *
+ * Applying a mapping factory to all Java types in all MXBean interfaces
+ * in a package, say {@code com.example.mxbeans}, looks like this. In the
+ * package source directory, create a file called {@code package-info.java}
+ * with these contents:
+ *
+ *
+ * {@literal @MXBeanMappingFactoryClass}(MyLinkedListMappingFactory.class)
+ * package com.example.mxbeans;
+ *
+ *
+ * @see MXBeanMappingFactory
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.PACKAGE})
+@Documented @Inherited
+public @interface MXBeanMappingFactoryClass {
+ /**
+ * The {@link MXBeanMappingFactory} class to be used to map
+ * types in the annotated interface or package. This class must
+ * have a public constructor with no arguments. See the {@code
+ * MXBeanMappingFactory} documentation for an example.
+ */
+ public Class extends MXBeanMappingFactory> value();
+}
diff --git a/src/share/classes/javax/management/openmbean/OpenType.java b/src/share/classes/javax/management/openmbean/OpenType.java
index 34d8e925c8cf7bb089b15a0082a05609d38601b1..5de8607a7e017f3d778a01a6e9d66d4bfe34e3f8 100644
--- a/src/share/classes/javax/management/openmbean/OpenType.java
+++ b/src/share/classes/javax/management/openmbean/OpenType.java
@@ -219,7 +219,7 @@ public abstract class OpenType implements Serializable {
});
}
- private static String validClassName(String className) throws OpenDataException {
+ static String validClassName(String className) throws OpenDataException {
className = valid("className", className);
// Check if className describes an array class, and determines its elements' class name.
diff --git a/test/javax/management/mxbean/CustomTypeTest.java b/test/javax/management/mxbean/CustomTypeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ffcb11a33942ed4f93d6b1ef259bbe1b3878c790
--- /dev/null
+++ b/test/javax/management/mxbean/CustomTypeTest.java
@@ -0,0 +1,590 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test %M% %I%
+ * @bug 6562936
+ * @run compile customtypes/package-info.java
+ * @run main CustomTypeTest
+ */
+
+import java.io.InvalidObjectException;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+import javax.management.Descriptor;
+import javax.management.MBeanServerInvocationHandler;
+import javax.management.NotCompliantMBeanException;
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.MXBeanMapping;
+import javax.management.openmbean.MXBeanMappingClass;
+import javax.management.openmbean.MXBeanMappingFactory;
+import javax.management.openmbean.MXBeanMappingFactoryClass;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+
+import static javax.management.JMX.MBeanOptions;
+
+import customtypes.*;
+
+public class CustomTypeTest {
+ @MXBeanMappingClass(LinkedListMapping.class)
+ public static class LinkedList {
+ private final String name;
+ private final LinkedList next;
+
+ public LinkedList(String name, LinkedList next) {
+ this.name = name;
+ this.next = next;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public LinkedList getNext() {
+ return next;
+ }
+
+ public String toString() {
+ if (next == null)
+ return "(" + name + ")";
+ else
+ return "(" + name + " " + next + ")";
+ }
+
+ public boolean equals(Object x) {
+ if (!(x instanceof LinkedList))
+ return false;
+ LinkedList other = (LinkedList) x;
+ return (this.name.equals(other.name) &&
+ (this.next == null ? other.next == null :
+ this.next.equals(other.next)));
+ }
+ }
+
+ public static class LinkedListMapping extends MXBeanMapping {
+ public LinkedListMapping(Type type) throws OpenDataException {
+ super(LinkedList.class, ArrayType.getArrayType(SimpleType.STRING));
+ if (type != LinkedList.class) {
+ throw new OpenDataException("Mapping only valid for " +
+ LinkedList.class);
+ }
+ }
+
+ public Object fromOpenValue(Object openValue) throws InvalidObjectException {
+ String[] array = (String[]) openValue;
+ LinkedList list = null;
+ for (int i = array.length - 1; i >= 0; i--)
+ list = new LinkedList(array[i], list);
+ return list;
+ }
+
+ public Object toOpenValue(Object javaValue) throws OpenDataException {
+ ArrayList array = new ArrayList();
+ for (LinkedList list = (LinkedList) javaValue; list != null;
+ list = list.getNext())
+ array.add(list.getName());
+ return array.toArray(new String[0]);
+ }
+ }
+
+ public static interface LinkedListMXBean {
+ public LinkedList getLinkedList();
+ }
+
+ public static class LinkedListImpl implements LinkedListMXBean {
+ public LinkedList getLinkedList() {
+ return new LinkedList("car", new LinkedList("cdr", null));
+ }
+ }
+
+ public static class ObjectMXBeanMapping extends MXBeanMapping {
+ private static final CompositeType wildcardType;
+
+ static {
+ try {
+ wildcardType =
+ new CompositeType(Object.class.getName(),
+ "Wildcard type for Object",
+ new String[0], // itemNames
+ new String[0], // itemDescriptions
+ new OpenType>[0]); // itemTypes
+ } catch (OpenDataException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public ObjectMXBeanMapping() {
+ super(Object.class, wildcardType);
+ }
+
+ public Object fromOpenValue(Object openValue) throws InvalidObjectException {
+ if (!(openValue instanceof CompositeData)) {
+ throw new InvalidObjectException("Not a CompositeData: " +
+ openValue.getClass());
+ }
+ CompositeData cd = (CompositeData) openValue;
+ if (!cd.containsKey("value")) {
+ throw new InvalidObjectException("CompositeData does not " +
+ "contain a \"value\" item: " + cd);
+ }
+ Object x = cd.get("value");
+ if (!(x instanceof CompositeData || x instanceof TabularData ||
+ x instanceof Object[]))
+ return x;
+
+ String typeName = (String) cd.get("type");
+ if (typeName == null) {
+ throw new InvalidObjectException("CompositeData does not " +
+ "contain a \"type\" item: " + cd);
+ }
+ Class> c;
+ try {
+ c = Class.forName(typeName);
+ } catch (ClassNotFoundException e) {
+ InvalidObjectException ioe =
+ new InvalidObjectException("Could not find type");
+ ioe.initCause(e);
+ throw ioe;
+ }
+ MXBeanMapping mapping;
+ try {
+ mapping = objectMappingFactory.mappingForType(c, objectMappingFactory);
+ } catch (OpenDataException e) {
+ InvalidObjectException ioe =
+ new InvalidObjectException("Could not map object's " +
+ "type " + c.getName());
+ ioe.initCause(e);
+ throw ioe;
+ }
+ return mapping.fromOpenValue(x);
+ }
+
+ public Object toOpenValue(Object javaValue) throws OpenDataException {
+ OpenType> openType;
+ Object openValue;
+ String typeName;
+ if (javaValue == null) {
+ openType = SimpleType.VOID;
+ openValue = null;
+ typeName = null;
+ } else {
+ Class> c = javaValue.getClass();
+ if (c.equals(Object.class))
+ throw new OpenDataException("Cannot map Object to an open value");
+ MXBeanMapping mapping =
+ objectMappingFactory.mappingForType(c, objectMappingFactory);
+ openType = mapping.getOpenType();
+ openValue = mapping.toOpenValue(javaValue);
+ typeName = c.getName();
+ }
+ CompositeType ct = new CompositeType(
+ (javaValue == null) ? "null" : openType.getClassName(),
+ "Open Mapping for Object",
+ new String[] {"type", "value"},
+ new String[] {"type", "value"},
+ new OpenType>[] {SimpleType.STRING, openType});
+ return new CompositeDataSupport(
+ ct,
+ new String[] {"type", "value"},
+ new Object[] {typeName, openValue});
+ }
+ }
+
+ public static class ObjectMappingFactory extends MXBeanMappingFactory {
+ private static MXBeanMapping objectMapping =
+ new ObjectMXBeanMapping();
+
+ @Override
+ public MXBeanMapping mappingForType(Type t, MXBeanMappingFactory f)
+ throws OpenDataException {
+ if (t.equals(Object.class))
+ return objectMapping;
+ else
+ return MXBeanMappingFactory.DEFAULT.mappingForType(t, f);
+ }
+ }
+
+ private static MXBeanMappingFactory objectMappingFactory =
+ new ObjectMappingFactory();
+
+ public static interface ObjectMXBean {
+ public Object getObject();
+ public Object[] getObjects();
+ public List getObjectList();
+ public Object[][] getMoreObjects();
+ }
+
+ public static class ObjectImpl implements ObjectMXBean {
+ public Object getObject() {
+ return 123;
+ }
+
+ private static Object[] objects = {
+ "foo", 3, 3.14f, 3.14, 3L, new Date(), ObjectName.WILDCARD,
+ new byte[3], new char[3], new int[3][3],
+ new LinkedListImpl().getLinkedList(),
+ };
+
+ public Object[] getObjects() {
+ return objects;
+ }
+
+ public List getObjectList() {
+ return Arrays.asList(getObjects());
+ }
+
+ public Object[][] getMoreObjects() {
+ return new Object[][] {{getObjects()}};
+ }
+ }
+
+ @MXBeanMappingFactoryClass(ObjectMappingFactory.class)
+ public static interface AnnotatedObjectMXBean extends ObjectMXBean {}
+
+ public static class AnnotatedObjectImpl extends ObjectImpl
+ implements AnnotatedObjectMXBean {}
+
+ public static class BrokenMappingFactory extends MXBeanMappingFactory {
+ public MXBeanMapping mappingForType(Type t, MXBeanMappingFactory f)
+ throws OpenDataException {
+ throw new OpenDataException(t.toString());
+ }
+ }
+
+ public static class ReallyBrokenMappingFactory extends BrokenMappingFactory {
+ public ReallyBrokenMappingFactory() {
+ throw new RuntimeException("Oops");
+ }
+ }
+
+ @MXBeanMappingFactoryClass(BrokenMappingFactory.class)
+ public static interface BrokenMXBean {
+ public int getX();
+ }
+
+ public static class BrokenImpl implements BrokenMXBean {
+ public int getX() {return 0;}
+ }
+
+ @MXBeanMappingFactoryClass(ReallyBrokenMappingFactory.class)
+ public static interface ReallyBrokenMXBean {
+ public int getX();
+ }
+
+ public static class ReallyBrokenImpl implements ReallyBrokenMXBean {
+ public int getX() {return 0;}
+ }
+
+ public static class BrokenMapping extends MXBeanMapping {
+ public BrokenMapping(Type t) {
+ super(t, SimpleType.STRING);
+ throw new RuntimeException("Oops");
+ }
+
+ public Object fromOpenValue(Object openValue) throws InvalidObjectException {
+ throw new AssertionError();
+ }
+
+ public Object toOpenValue(Object javaValue) throws OpenDataException {
+ throw new AssertionError();
+ }
+ }
+
+ @MXBeanMappingClass(BrokenMapping.class)
+ public static class BrokenType {}
+
+ public static interface BrokenTypeMXBean {
+ BrokenType getBroken();
+ }
+
+ public static class BrokenTypeImpl implements BrokenTypeMXBean {
+ public BrokenType getBroken() {
+ throw new AssertionError();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+
+ System.out.println("Test @MXBeanMappingClass");
+ ObjectName linkedName = new ObjectName("d:type=LinkedList");
+ LinkedListMXBean linkedListMXBean = new LinkedListImpl();
+ LinkedList list1 = linkedListMXBean.getLinkedList();
+ mbs.registerMBean(linkedListMXBean, linkedName);
+ LinkedListMXBean linkedProxy =
+ JMX.newMXBeanProxy(mbs, linkedName, LinkedListMXBean.class);
+ MBeanServerInvocationHandler mbsih = (MBeanServerInvocationHandler)
+ Proxy.getInvocationHandler(linkedProxy);
+ if (!mbsih.isMXBean())
+ fail("not MXBean proxy");
+ LinkedList list2 = linkedProxy.getLinkedList();
+ if (list1 == list2)
+ fail("lists identical!");
+ // They should have gone through the mapping and back,
+ // and the mapping doesn't do anything that would allow it
+ // to restore the identical object.
+ if (!list1.equals(list2))
+ fail("lists different: " + list1 + " vs " + list2);
+ System.out.println("...success");
+
+ System.out.println("Test StandardMBean with MXBeanMappingFactory");
+ ObjectMXBean wildcardMBean = new ObjectImpl();
+ MBeanOptions options = new MBeanOptions();
+ options.setMXBeanMappingFactory(objectMappingFactory);
+ if (!options.isMXBean())
+ fail("Setting MXBeanMappingFactory should imply MXBean");
+ StandardMBean wildcardStandardMBean =
+ new StandardMBean(wildcardMBean, ObjectMXBean.class, options);
+ testWildcardMBean(mbs, wildcardMBean, wildcardStandardMBean,
+ options, ObjectMXBean.class);
+
+ System.out.println("Test @MXBeanMappingFactoryClass on interface");
+ ObjectMXBean annotatedWildcardMBean = new AnnotatedObjectImpl();
+ testWildcardMBean(mbs, annotatedWildcardMBean, annotatedWildcardMBean,
+ null, AnnotatedObjectMXBean.class);
+
+ System.out.println("Test @MXBeanMappingFactoryClass on package");
+ CustomMXBean custom = zeroProxy(CustomMXBean.class);
+ ObjectName customName = new ObjectName("d:type=Custom");
+ mbs.registerMBean(custom, customName);
+ Object x = mbs.getAttribute(customName, "X");
+ if (!(x instanceof String))
+ fail("Should be String: " + x + " (a " + x.getClass().getName() + ")");
+ CustomMXBean customProxy =
+ JMX.newMXBeanProxy(mbs, customName, CustomMXBean.class);
+ x = customProxy.getX();
+ if (!(x instanceof Integer) || (Integer) x != 0)
+ fail("Wrong return from proxy: " + x + " (a " + x.getClass().getName() + ")");
+
+ System.out.println("Test MXBeanMappingFactory exception");
+ try {
+ mbs.registerMBean(new BrokenImpl(), new ObjectName("d:type=Broken"));
+ fail("Register did not throw exception");
+ } catch (NotCompliantMBeanException e) {
+ System.out.println("...OK: threw: " + e);
+ }
+
+ System.out.println("Test MXBeanMappingFactory constructor exception");
+ try {
+ mbs.registerMBean(new ReallyBrokenImpl(), new ObjectName("d:type=Broken"));
+ fail("Register did not throw exception");
+ } catch (IllegalArgumentException e) {
+ System.out.println("...OK: threw: " + e);
+ }
+
+ System.out.println("Test MXBeanMappingFactory exception with StandardMBean");
+ MXBeanMappingFactory brokenF = new BrokenMappingFactory();
+ MBeanOptions brokenO = new MBeanOptions();
+ brokenO.setMXBeanMappingFactory(brokenF);
+ try {
+ new StandardMBean(wildcardMBean, ObjectMXBean.class, brokenO);
+ fail("StandardMBean with broken factory did not throw exception");
+ } catch (IllegalArgumentException e) {
+ if (!(e.getCause() instanceof NotCompliantMBeanException)) {
+ fail("StandardMBean with broken factory threw wrong exception: "
+ + e.getCause());
+ }
+ }
+
+ System.out.println("Test MXBeanMappingClass exception");
+ try {
+ mbs.registerMBean(new BrokenTypeImpl(), new ObjectName("d:type=Broken"));
+ fail("Broken MXBeanMappingClass did not throw exception");
+ } catch (NotCompliantMBeanException e) {
+ System.out.println("...OK: threw: " + e);
+ }
+
+ if (failure == null)
+ System.out.println("TEST PASSED");
+ else
+ throw new Exception("TEST FAILED: " + failure);
+ }
+
+ private static void testWildcardMBean(MBeanServer mbs, ObjectMXBean impl,
+ Object mbean,
+ MBeanOptions proxyOptions,
+ Class extends ObjectMXBean> intf)
+ throws Exception {
+ ObjectName wildcardName = new ObjectName("d:type=Object");
+ mbs.registerMBean(mbean, wildcardName);
+ try {
+ testWildcardMBean2(mbs, impl, wildcardName, proxyOptions, intf);
+ } finally {
+ mbs.unregisterMBean(wildcardName);
+ }
+ }
+
+ private static void testWildcardMBean2(MBeanServer mbs, ObjectMXBean impl,
+ ObjectName wildcardName,
+ MBeanOptions proxyOptions,
+ Class extends ObjectMXBean> intf)
+ throws Exception {
+ if (proxyOptions == null) {
+ proxyOptions = new MBeanOptions();
+ MXBeanMappingFactory f = MXBeanMappingFactory.forInterface(intf);
+ proxyOptions.setMXBeanMappingFactory(f);
+ }
+ Descriptor d = mbs.getMBeanInfo(wildcardName).getDescriptor();
+ String factoryName = (String)
+ d.getFieldValue(JMX.MXBEAN_MAPPING_FACTORY_CLASS_FIELD);
+ if (!ObjectMappingFactory.class.getName().equals(factoryName)) {
+ fail("Descriptor has wrong MXBeanMappingFactory: " + factoryName +
+ " should be " + ObjectMappingFactory.class.getName());
+ }
+ ObjectMXBean wildcardProxy =
+ JMX.newMBeanProxy(mbs, wildcardName, intf, proxyOptions);
+ MBeanServerInvocationHandler mbsih = (MBeanServerInvocationHandler)
+ Proxy.getInvocationHandler(wildcardProxy);
+ MBeanOptions opts = mbsih.getMBeanOptions();
+ if (!opts.equals(proxyOptions)) {
+ fail("Proxy options differ from request: " + opts + " vs " +
+ proxyOptions);
+ }
+ Method[] wildcardMethods = ObjectMXBean.class.getMethods();
+ for (Method m : wildcardMethods) {
+ System.out.println("..." + m.getName());
+ Object orig = m.invoke(impl);
+ Object copy = m.invoke(wildcardProxy);
+ if (!deepEquals(orig, copy)) {
+ fail("objects differ: " + deepToString(orig) + " vs " +
+ deepToString(copy));
+ }
+ }
+ }
+
+ private static T zeroProxy(Class intf) {
+ return intf.cast(Proxy.newProxyInstance(intf.getClassLoader(),
+ new Class>[] {intf},
+ new ZeroInvocationHandler()));
+ }
+
+ private static class ZeroInvocationHandler implements InvocationHandler {
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ return 0;
+ }
+ }
+
+ private static boolean deepEquals(Object x, Object y) {
+ if (x == y)
+ return true;
+ if (x == null || y == null)
+ return false;
+
+ if (x instanceof Collection>) {
+ if (!(y instanceof Collection>))
+ return false;
+ Collection> xcoll = (Collection>) x;
+ Collection> ycoll = (Collection>) y;
+ if (xcoll.size() != ycoll.size())
+ return false;
+ Iterator> xit = xcoll.iterator();
+ Iterator> yit = ycoll.iterator();
+ while (xit.hasNext()) {
+ if (!deepEquals(xit.next(), yit.next()))
+ return false;
+ }
+ return true;
+ }
+
+ Class> xclass = x.getClass();
+ Class> yclass = y.getClass();
+ if (xclass.isArray()) {
+ if (!yclass.isArray())
+ return false;
+ if (!xclass.getComponentType().equals(yclass.getComponentType()))
+ return false;
+ int len = Array.getLength(x);
+ if (Array.getLength(y) != len)
+ return false;
+ for (int i = 0; i < len; i++) {
+ if (!deepEquals(Array.get(x, i), Array.get(y, i)))
+ return false;
+ }
+ return true;
+ }
+
+// return x.equals(y);
+ if (x.equals(y))
+ return true;
+ System.out.println("Not equal: <" + x + "> and <" + y + ">");
+ return false;
+ }
+
+ private static String deepToString(Object x) {
+ if (x == null)
+ return "null";
+
+ if (x instanceof Collection>) {
+ Collection> xcoll = (Collection>) x;
+ StringBuilder sb = new StringBuilder("[");
+ for (Object e : xcoll) {
+ if (sb.length() > 1)
+ sb.append(", ");
+ sb.append(deepToString(e));
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ if (x instanceof Object[]) {
+ Object[] xarr = (Object[]) x;
+ return deepToString(Arrays.asList(xarr));
+ }
+
+ if (x.getClass().isArray()) { // primitive array
+ String s = Arrays.deepToString(new Object[] {x});
+ return s.substring(1, s.length() - 1);
+ }
+
+ return x.toString();
+ }
+
+ private static void fail(String msg) {
+ System.out.println("TEST FAILED: " + msg);
+ if (msg.length() > 100)
+ msg = msg.substring(0, 100) + "...";
+ failure = msg;
+ }
+
+ private static String failure;
+}
diff --git a/test/javax/management/mxbean/customtypes/CustomLongMXBean.java b/test/javax/management/mxbean/customtypes/CustomLongMXBean.java
new file mode 100644
index 0000000000000000000000000000000000000000..5605967b761a71a3020e524b666d00b42e44c80d
--- /dev/null
+++ b/test/javax/management/mxbean/customtypes/CustomLongMXBean.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+// CustomLongMXBean.java - see CustomTypeTest
+
+package customtypes;
+
+import javax.management.openmbean.MXBeanMappingFactoryClass;
+
+@MXBeanMappingFactoryClass(IntegerIsLongFactory.class)
+public interface CustomLongMXBean extends CustomMXBean {}
diff --git a/src/share/classes/javax/management/ToQueryString.java b/test/javax/management/mxbean/customtypes/CustomMXBean.java
similarity index 56%
rename from src/share/classes/javax/management/ToQueryString.java
rename to test/javax/management/mxbean/customtypes/CustomMXBean.java
index be73bc5f3777c3a18ca9ecdc2a875826b75be8f1..6cc17de9e6cc06876b563788e923d6597d789cf5 100644
--- a/src/share/classes/javax/management/ToQueryString.java
+++ b/test/javax/management/mxbean/customtypes/CustomMXBean.java
@@ -1,12 +1,10 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
+ * published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -23,16 +21,10 @@
* have any questions.
*/
-package javax.management;
+// CustomMXBean.java - see CustomTypeTest
-/* QueryExp classes can extend this to get non-default treatment for
- * Query.toString(q). We're reluctant to change the public toString()
- * methods of the classes because people might be parsing them, even
- * though that's rather fragile. But Query.toString(q) has no such
- * constraint so it can use the new toQueryString() method defined here.
- */
-class ToQueryString {
- String toQueryString() {
- return toString();
- }
+package customtypes;
+
+public interface CustomMXBean {
+ public Integer getX();
}
diff --git a/test/javax/management/mxbean/customtypes/IntegerIsLongFactory.java b/test/javax/management/mxbean/customtypes/IntegerIsLongFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5f3ac5b97500c0b9c9d59119757fa5eb377eb05
--- /dev/null
+++ b/test/javax/management/mxbean/customtypes/IntegerIsLongFactory.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+// IntegerIsLongFactory.java - see CustomTypeTest
+
+package customtypes;
+
+import java.io.InvalidObjectException;
+import java.lang.reflect.Type;
+import javax.management.openmbean.MXBeanMapping;
+import javax.management.openmbean.MXBeanMappingFactory;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.SimpleType;
+
+public class IntegerIsLongFactory implements MXBeanMappingFactory {
+ public MXBeanMapping forType(Type t, MXBeanMappingFactory f)
+ throws OpenDataException {
+ if (t == Integer.class)
+ return IntegerIsLongMapping;
+ else
+ return MXBeanMappingFactory.DEFAULT.forType(t, f);
+ }
+
+ private static final MXBeanMapping IntegerIsLongMapping =
+ new IntegerIsLongMapping();
+
+ private static class IntegerIsLongMapping extends MXBeanMapping {
+ IntegerIsLongMapping() {
+ super(Integer.class, SimpleType.STRING);
+ }
+
+ public Object fromOpenValue(Object openValue)
+ throws InvalidObjectException {
+ try {
+ return (Long) openValue;
+ } catch (Exception e) {
+ InvalidObjectException ioe = new InvalidObjectException("oops");
+ ioe.initCause(e);
+ throw ioe;
+ }
+ }
+
+ public Object toOpenValue(Object javaValue) throws OpenDataException {
+ try {
+ Integer i = (Integer) javaValue;
+ return new Long((int) i);
+ } catch (Exception e) {
+ OpenDataException ode = new OpenDataException("oops");
+ ode.initCause(e);
+ throw ode;
+ }
+ }
+ }
+}
diff --git a/test/javax/management/mxbean/customtypes/IntegerIsStringFactory.java b/test/javax/management/mxbean/customtypes/IntegerIsStringFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..6863da68066368544ea88040657c1285ad39432d
--- /dev/null
+++ b/test/javax/management/mxbean/customtypes/IntegerIsStringFactory.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+// IntegerIsStringFactory.java - see CustomTypeTest
+
+package customtypes;
+
+import java.io.InvalidObjectException;
+import java.lang.reflect.Type;
+import javax.management.openmbean.MXBeanMapping;
+import javax.management.openmbean.MXBeanMappingFactory;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.SimpleType;
+
+public class IntegerIsStringFactory extends MXBeanMappingFactory {
+ @Override
+ public MXBeanMapping mappingForType(Type t, MXBeanMappingFactory f)
+ throws OpenDataException {
+ if (t == Integer.class)
+ return integerIsStringMapping;
+ else
+ return MXBeanMappingFactory.DEFAULT.mappingForType(t, f);
+ }
+
+ private static final MXBeanMapping integerIsStringMapping =
+ new IntegerIsStringMapping();
+
+ private static class IntegerIsStringMapping extends MXBeanMapping {
+ IntegerIsStringMapping() {
+ super(Integer.class, SimpleType.STRING);
+ }
+
+ public Object fromOpenValue(Object openValue)
+ throws InvalidObjectException {
+ try {
+ String s = (String) openValue;
+ return Integer.parseInt(s);
+ } catch (Exception e) {
+ InvalidObjectException ioe = new InvalidObjectException("oops");
+ ioe.initCause(e);
+ throw ioe;
+ }
+ }
+
+ public Object toOpenValue(Object javaValue) throws OpenDataException {
+ try {
+ Integer i = (Integer) javaValue;
+ return i.toString();
+ } catch (Exception e) {
+ OpenDataException ode = new OpenDataException("oops");
+ ode.initCause(e);
+ throw ode;
+ }
+ }
+ }
+}
diff --git a/test/javax/management/mxbean/customtypes/package-info.java b/test/javax/management/mxbean/customtypes/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..97d2aa57fb17f7c241baa43ebddb1328ffdb4897
--- /dev/null
+++ b/test/javax/management/mxbean/customtypes/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+// package-info.java - test package annotations for custom types
+
+@javax.management.openmbean.MXBeanMappingFactoryClass(IntegerIsStringFactory.class)
+package customtypes;