6323980: Annotations to simplify MBean development

Reviewed-by: jfdenise, dfuchs
This commit is contained in:
Eamonn McManus 2008-07-09 10:36:07 +02:00
parent 5967d518b5
commit 4333dd3520
46 changed files with 5419 additions and 587 deletions

View File

@ -84,8 +84,13 @@ import com.sun.jmx.mbeanserver.MBeanInstantiator;
import com.sun.jmx.mbeanserver.Repository;
import com.sun.jmx.mbeanserver.NamedObject;
import com.sun.jmx.mbeanserver.Introspector;
import com.sun.jmx.mbeanserver.MBeanInjector;
import com.sun.jmx.mbeanserver.NotifySupport;
import com.sun.jmx.mbeanserver.Repository.RegistrationContext;
import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.remote.util.EnvHelp;
import javax.management.DynamicWrapperMBean;
import javax.management.NotificationBroadcasterSupport;
/**
* This is the default class for MBean manipulation on the agent side. It
@ -433,36 +438,26 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
if (instance instanceof MBeanRegistration)
preDeregisterInvoke((MBeanRegistration) instance);
repository.remove(name);
// may throw InstanceNotFoundException
final Object resource = getResource(instance);
/**
* Checks if the unregistered MBean is a ClassLoader
* If so, it removes the MBean from the default loader repository.
*/
// Unregisters the MBean from the repository.
// Returns the resource context that was used.
// The returned context does nothing for regular MBeans.
// For ClassLoader MBeans and JMXNamespace (and JMXDomain)
// MBeans - the context makes it possible to unregister these
// objects from the appropriate framework artifacts, such as
// the CLR or the dispatcher, from within the repository lock.
// In case of success, we also need to call context.done() at the
// end of this method.
//
final ResourceContext context =
unregisterFromRepository(resource, instance, name);
Object resource = getResource(instance);
if (resource instanceof ClassLoader
&& resource != server.getClass().getClassLoader()) {
final ModifiableClassLoaderRepository clr =
instantiator.getClassLoaderRepository();
if (clr != null) clr.removeClassLoader(name);
}
// ---------------------
// Send deletion event
// ---------------------
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
MBEANSERVER_LOGGER.logp(Level.FINER,
DefaultMBeanServerInterceptor.class.getName(),
"unregisterMBean", "Send delete notification of object " +
name.getCanonicalName());
}
sendNotification(MBeanServerNotification.UNREGISTRATION_NOTIFICATION,
name);
if (instance instanceof MBeanRegistration)
postDeregisterInvoke((MBeanRegistration) instance);
context.done();
}
public ObjectInstance getObjectInstance(ObjectName name)
@ -939,15 +934,22 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
ObjectName logicalName = name;
logicalName = preRegister(mbean, server, name);
// preRegister returned successfully, so from this point on we
// must call postRegister(false) if there is any problem.
boolean registered = false;
boolean registerFailed = false;
ResourceContext context = null;
try {
mbean = injectResources(mbean, server, logicalName);
if (mbean instanceof MBeanRegistration) {
MBeanRegistration reg = (MBeanRegistration) mbean;
logicalName = preRegisterInvoke(reg, name, server);
if (mbean instanceof DynamicMBean2) {
try {
((DynamicMBean2) mbean).preRegister2(server, logicalName);
registerFailed = true; // until we succeed
} catch (Exception e) {
postRegisterInvoke(reg, false, false);
if (e instanceof RuntimeException)
throw (RuntimeException) e;
if (e instanceof InstanceAlreadyExistsException)
@ -960,86 +962,102 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
logicalName =
ObjectName.getInstance(nonDefaultDomain(logicalName));
}
}
checkMBeanPermission(classname, null, logicalName, "registerMBean");
checkMBeanPermission(classname, null, logicalName, "registerMBean");
final ObjectInstance result;
if (logicalName!=null) {
result = new ObjectInstance(logicalName, classname);
internal_addObject(mbean, logicalName);
} else {
if (mbean instanceof MBeanRegistration)
postRegisterInvoke((MBeanRegistration) mbean, false, true);
final RuntimeException wrapped =
new IllegalArgumentException("No object name specified");
throw new RuntimeOperationsException(wrapped,
"Exception occurred trying to register the MBean");
}
if (mbean instanceof MBeanRegistration)
postRegisterInvoke((MBeanRegistration) mbean, true, false);
/**
* Checks if the newly registered MBean is a ClassLoader
* If so, tell the ClassLoaderRepository (CLR) about it. We do
* this even if the object is a PrivateClassLoader. In that
* case, the CLR remembers the loader for use when it is
* explicitly named (e.g. as the loader in createMBean) but
* does not add it to the list that is consulted by
* ClassLoaderRepository.loadClass.
*/
final Object resource = getResource(mbean);
if (resource instanceof ClassLoader) {
final ModifiableClassLoaderRepository clr =
instantiator.getClassLoaderRepository();
if (clr == null) {
if (logicalName == null) {
final RuntimeException wrapped =
new IllegalArgumentException(
"Dynamic addition of class loaders is not supported");
new IllegalArgumentException("No object name specified");
throw new RuntimeOperationsException(wrapped,
"Exception occurred trying to register the MBean as a class loader");
"Exception occurred trying to register the MBean");
}
clr.addClassLoader(logicalName, (ClassLoader) resource);
final Object resource = getResource(mbean);
// Register the MBean with the repository.
// Returns the resource context that was used.
// The returned context does nothing for regular MBeans.
// For ClassLoader MBeans and JMXNamespace (and JMXDomain)
// MBeans - the context makes it possible to register these
// objects with the appropriate framework artifacts, such as
// the CLR or the dispatcher, from within the repository lock.
// In case of success, we also need to call context.done() at the
// end of this method.
//
context = registerWithRepository(resource, mbean, logicalName);
registerFailed = false;
registered = true;
} finally {
postRegister(mbean, registered, registerFailed);
}
return result;
context.done();
return new ObjectInstance(logicalName, classname);
}
private static ObjectName preRegisterInvoke(MBeanRegistration moi,
ObjectName name,
MBeanServer mbs)
throws InstanceAlreadyExistsException, MBeanRegistrationException {
final ObjectName newName;
private static void throwMBeanRegistrationException(Throwable t, String where)
throws MBeanRegistrationException {
try {
newName = moi.preRegister(mbs, name);
throw t;
} catch (RuntimeException e) {
throw new RuntimeMBeanException(e,
"RuntimeException thrown in preRegister method");
throw new RuntimeMBeanException(
e, "RuntimeException thrown " + where);
} catch (Error er) {
throw new RuntimeErrorException(er,
"Error thrown in preRegister method");
throw new RuntimeErrorException(er, "Error thrown " + where);
} catch (MBeanRegistrationException r) {
throw r;
} catch (Exception ex) {
throw new MBeanRegistrationException(ex,
"Exception thrown in preRegister method");
throw new MBeanRegistrationException(ex, "Exception thrown " + where);
} catch (Throwable t1) {
throw new RuntimeException(t); // neither Error nor Exception??
}
}
private static ObjectName preRegister(
DynamicMBean mbean, MBeanServer mbs, ObjectName name)
throws InstanceAlreadyExistsException, MBeanRegistrationException {
ObjectName newName = null;
try {
if (mbean instanceof MBeanRegistration)
newName = ((MBeanRegistration) mbean).preRegister(mbs, name);
} catch (Throwable t) {
throwMBeanRegistrationException(t, "in preRegister method");
}
if (newName != null) return newName;
else return name;
}
private static void postRegisterInvoke(MBeanRegistration moi,
boolean registrationDone,
boolean registerFailed) {
if (registerFailed && moi instanceof DynamicMBean2)
((DynamicMBean2) moi).registerFailed();
private static DynamicMBean injectResources(
DynamicMBean mbean, MBeanServer mbs, ObjectName name)
throws MBeanRegistrationException {
try {
moi.postRegister(registrationDone);
Object resource = getResource(mbean);
MBeanInjector.inject(resource, mbs, name);
if (MBeanInjector.injectsSendNotification(resource)) {
NotificationBroadcasterSupport nbs =
new NotificationBroadcasterSupport();
MBeanInjector.injectSendNotification(resource, nbs);
mbean = NotifySupport.wrap(mbean, nbs);
}
return mbean;
} catch (Throwable t) {
throwMBeanRegistrationException(t, "injecting @Resources");
return null; // not reached
}
}
private static void postRegister(
DynamicMBean mbean, boolean registrationDone, boolean registerFailed) {
if (registerFailed && mbean instanceof DynamicMBean2)
((DynamicMBean2) mbean).registerFailed();
try {
if (mbean instanceof MBeanRegistration)
((MBeanRegistration) mbean).postRegister(registrationDone);
} catch (RuntimeException e) {
throw new RuntimeMBeanException(e,
"RuntimeException thrown in postRegister method");
@ -1053,17 +1071,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
throws MBeanRegistrationException {
try {
moi.preDeregister();
} catch (RuntimeException e) {
throw new RuntimeMBeanException(e,
"RuntimeException thrown in preDeregister method");
} catch (Error er) {
throw new RuntimeErrorException(er,
"Error thrown in preDeregister method");
} catch (MBeanRegistrationException t) {
throw t;
} catch (Exception ex) {
throw new MBeanRegistrationException(ex,
"Exception thrown in preDeregister method");
} catch (Throwable t) {
throwMBeanRegistrationException(t, "in preDeregister method");
}
}
@ -1104,12 +1113,19 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
private static Object getResource(DynamicMBean mbean) {
if (mbean instanceof DynamicMBean2)
return ((DynamicMBean2) mbean).getResource();
if (mbean instanceof DynamicWrapperMBean)
return ((DynamicWrapperMBean) mbean).getWrappedObject();
else
return mbean;
}
private static ClassLoader getResourceLoader(DynamicMBean mbean) {
if (mbean instanceof DynamicWrapperMBean)
return ((DynamicWrapperMBean) mbean).getWrappedClassLoader();
else
return mbean.getClass().getClassLoader();
}
private ObjectName nonDefaultDomain(ObjectName name) {
if (name == null || name.getDomain().length() > 0)
return name;
@ -1123,14 +1139,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
if one is supplied where it shouldn't be). */
final String completeName = domain + name;
try {
return new ObjectName(completeName);
} catch (MalformedObjectNameException e) {
final String msg =
"Unexpected default domain problem: " + completeName + ": " +
e;
throw EnvHelp.initCause(new IllegalArgumentException(msg), e);
}
return Util.newObjectName(completeName);
}
public String getDefaultDomain() {
@ -1211,7 +1220,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
NotificationListener listenerWrapper =
getListenerWrapper(listener, name, broadcaster, true);
getListenerWrapper(listener, name, instance, true);
broadcaster.addNotificationListener(listenerWrapper, filter, handback);
}
@ -1335,7 +1344,6 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
DynamicMBean instance = getMBean(name);
checkMBeanPermission(instance, null, name,
"removeNotificationListener");
Object resource = getResource(instance);
/* We could simplify the code by assigning broadcaster after
assigning listenerWrapper, but that would change the error
@ -1348,7 +1356,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
getNotificationBroadcaster(name, instance, reqClass);
NotificationListener listenerWrapper =
getListenerWrapper(listener, name, resource, false);
getListenerWrapper(listener, name, instance, false);
if (listenerWrapper == null)
throw new ListenerNotFoundException("Unknown listener");
@ -1366,8 +1374,10 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
private static <T extends NotificationBroadcaster>
T getNotificationBroadcaster(ObjectName name, Object instance,
Class<T> reqClass) {
if (instance instanceof DynamicMBean2)
instance = ((DynamicMBean2) instance).getResource();
if (reqClass.isInstance(instance))
return reqClass.cast(instance);
if (instance instanceof DynamicWrapperMBean)
instance = ((DynamicWrapperMBean) instance).getWrappedObject();
if (reqClass.isInstance(instance))
return reqClass.cast(instance);
final RuntimeException exc =
@ -1415,24 +1425,31 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
checkMBeanPermission(instance, null, name, "isInstanceOf");
try {
if (instance instanceof DynamicMBean2) {
Object resource = ((DynamicMBean2) instance).getResource();
ClassLoader loader = resource.getClass().getClassLoader();
Class<?> c = Class.forName(className, false, loader);
return c.isInstance(resource);
}
Object resource = getResource(instance);
final String cn = getClassName(instance);
if (cn.equals(className))
final String resourceClassName =
(resource instanceof DynamicMBean) ?
getClassName((DynamicMBean) resource) :
resource.getClass().getName();
if (resourceClassName.equals(className))
return true;
final ClassLoader cl = instance.getClass().getClassLoader();
final ClassLoader cl = getResourceLoader(instance);
final Class<?> classNameClass = Class.forName(className, false, cl);
if (classNameClass.isInstance(instance))
if (classNameClass.isInstance(resource))
return true;
final Class<?> instanceClass = Class.forName(cn, false, cl);
return classNameClass.isAssignableFrom(instanceClass);
// Ensure that isInstanceOf(NotificationEmitter) is true when
// the MBean is a NotificationEmitter by virtue of a @Resource
// annotation specifying a SendNotification resource.
// This is a hack.
if (instance instanceof NotificationBroadcaster &&
classNameClass.isAssignableFrom(NotificationEmitter.class))
return true;
final Class<?> resourceClass = Class.forName(resourceClassName, false, cl);
return classNameClass.isAssignableFrom(resourceClass);
} catch (Exception x) {
/* Could be SecurityException or ClassNotFoundException */
if (MBEANSERVER_LOGGER.isLoggable(Level.FINEST)) {
@ -1457,7 +1474,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
DynamicMBean instance = getMBean(mbeanName);
checkMBeanPermission(instance, null, mbeanName, "getClassLoaderFor");
return getResource(instance).getClass().getClassLoader();
return getResourceLoader(instance);
}
/**
@ -1488,40 +1505,6 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
return (ClassLoader) resource;
}
/**
* Adds a MBean in the repository
*/
private void internal_addObject(DynamicMBean object, ObjectName logicalName)
throws InstanceAlreadyExistsException {
// ------------------------------
// ------------------------------
// Let the repository do the work.
try {
repository.addMBean(object, logicalName);
} catch (InstanceAlreadyExistsException e) {
if (object instanceof MBeanRegistration) {
postRegisterInvoke((MBeanRegistration) object, false, true);
}
throw e;
}
// ---------------------
// Send create event
// ---------------------
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
MBEANSERVER_LOGGER.logp(Level.FINER,
DefaultMBeanServerInterceptor.class.getName(),
"addObject", "Send create notification of object " +
logicalName.getCanonicalName());
}
sendNotification(MBeanServerNotification.REGISTRATION_NOTIFICATION,
logicalName ) ;
}
/**
* Sends an MBeanServerNotifications with the specified type for the
* MBean with the specified ObjectName
@ -1712,9 +1695,10 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
*/
private NotificationListener getListenerWrapper(NotificationListener l,
ObjectName name,
Object mbean,
DynamicMBean mbean,
boolean create) {
ListenerWrapper wrapper = new ListenerWrapper(l, name, mbean);
Object resource = getResource(mbean);
ListenerWrapper wrapper = new ListenerWrapper(l, name, resource);
synchronized (listenerWrappers) {
WeakReference<ListenerWrapper> ref = listenerWrappers.get(wrapper);
if (ref != null) {
@ -1758,6 +1742,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
listener.handleNotification(notification, handback);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ListenerWrapper))
return false;
@ -1774,6 +1759,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
*/
}
@Override
public int hashCode() {
return (System.identityHashCode(listener) ^
System.identityHashCode(mbean));
@ -1851,4 +1837,213 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
}
// ------------------------------------------------------------------
//
// Dealing with registration of special MBeans in the repository.
//
// ------------------------------------------------------------------
/**
* A RegistrationContext that makes it possible to perform additional
* post registration actions (or post unregistration actions) outside
* of the repository lock, once postRegister (or postDeregister) has
* been called.
* The method {@code done()} will be called in registerMBean or
* unregisterMBean, at the end.
*/
private static interface ResourceContext extends RegistrationContext {
public void done();
/** An empty ResourceContext which does nothing **/
public static final ResourceContext NONE = new ResourceContext() {
public void done() {}
public void registering() {}
public void unregistered() {}
};
}
/**
* Adds a MBean in the repository,
* sends MBeanServerNotification.REGISTRATION_NOTIFICATION,
* returns ResourceContext for special resources such as ClassLoaders
* or JMXNamespaces. For regular MBean this method returns
* ResourceContext.NONE.
* @return a ResourceContext for special resources such as ClassLoaders
* or JMXNamespaces.
*/
private ResourceContext registerWithRepository(
final Object resource,
final DynamicMBean object,
final ObjectName logicalName)
throws InstanceAlreadyExistsException,
MBeanRegistrationException {
// Creates a registration context, if needed.
//
final ResourceContext context =
makeResourceContextFor(resource, logicalName);
repository.addMBean(object, logicalName, context);
// May throw InstanceAlreadyExistsException
// ---------------------
// Send create event
// ---------------------
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
MBEANSERVER_LOGGER.logp(Level.FINER,
DefaultMBeanServerInterceptor.class.getName(),
"addObject", "Send create notification of object " +
logicalName.getCanonicalName());
}
sendNotification(
MBeanServerNotification.REGISTRATION_NOTIFICATION,
logicalName);
return context;
}
/**
* Removes a MBean in the repository,
* sends MBeanServerNotification.UNREGISTRATION_NOTIFICATION,
* returns ResourceContext for special resources such as ClassLoaders
* or JMXNamespaces, or null. For regular MBean this method returns
* ResourceContext.NONE.
*
* @return a ResourceContext for special resources such as ClassLoaders
* or JMXNamespaces.
*/
private ResourceContext unregisterFromRepository(
final Object resource,
final DynamicMBean object,
final ObjectName logicalName)
throws InstanceNotFoundException {
// Creates a registration context, if needed.
//
final ResourceContext context =
makeResourceContextFor(resource, logicalName);
repository.remove(logicalName, context);
// ---------------------
// Send deletion event
// ---------------------
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
MBEANSERVER_LOGGER.logp(Level.FINER,
DefaultMBeanServerInterceptor.class.getName(),
"unregisterMBean", "Send delete notification of object " +
logicalName.getCanonicalName());
}
sendNotification(MBeanServerNotification.UNREGISTRATION_NOTIFICATION,
logicalName);
return context;
}
/**
* Registers a ClassLoader with the CLR.
* This method is called by the ResourceContext from within the
* repository lock.
* @param loader The ClassLoader.
* @param logicalName The ClassLoader MBean ObjectName.
*/
private void addClassLoader(ClassLoader loader,
final ObjectName logicalName) {
/**
* Called when the newly registered MBean is a ClassLoader
* If so, tell the ClassLoaderRepository (CLR) about it. We do
* this even if the loader is a PrivateClassLoader. In that
* case, the CLR remembers the loader for use when it is
* explicitly named (e.g. as the loader in createMBean) but
* does not add it to the list that is consulted by
* ClassLoaderRepository.loadClass.
*/
final ModifiableClassLoaderRepository clr =
instantiator.getClassLoaderRepository();
if (clr == null) {
final RuntimeException wrapped =
new IllegalArgumentException(
"Dynamic addition of class loaders" +
" is not supported");
throw new RuntimeOperationsException(wrapped,
"Exception occurred trying to register" +
" the MBean as a class loader");
}
clr.addClassLoader(logicalName, loader);
}
/**
* Unregisters a ClassLoader from the CLR.
* This method is called by the ResourceContext from within the
* repository lock.
* @param loader The ClassLoader.
* @param logicalName The ClassLoader MBean ObjectName.
*/
private void removeClassLoader(ClassLoader loader,
final ObjectName logicalName) {
/**
* Removes the MBean from the default loader repository.
*/
if (loader != server.getClass().getClassLoader()) {
final ModifiableClassLoaderRepository clr =
instantiator.getClassLoaderRepository();
if (clr != null) {
clr.removeClassLoader(logicalName);
}
}
}
/**
* Creates a ResourceContext for a ClassLoader MBean.
* The resource context makes it possible to add the ClassLoader to
* (ResourceContext.registering) or resp. remove the ClassLoader from
* (ResourceContext.unregistered) the CLR
* when the associated MBean is added to or resp. removed from the
* repository.
*
* @param loader The ClassLoader MBean being registered or
* unregistered.
* @param logicalName The name of the ClassLoader MBean.
* @return a ResourceContext that takes in charge the addition or removal
* of the loader to or from the CLR.
*/
private ResourceContext createClassLoaderContext(
final ClassLoader loader,
final ObjectName logicalName) {
return new ResourceContext() {
public void registering() {
addClassLoader(loader, logicalName);
}
public void unregistered() {
removeClassLoader(loader, logicalName);
}
public void done() {
}
};
}
/**
* Creates a ResourceContext for the given resource.
* If the resource does not need a ResourceContext, returns
* ResourceContext.NONE.
* At this time, only JMXNamespaces and ClassLoaders need a
* ResourceContext.
*
* @param resource The resource being registered or unregistered.
* @param logicalName The name of the associated MBean.
* @return
*/
private ResourceContext makeResourceContextFor(Object resource,
ObjectName logicalName) {
if (resource instanceof ClassLoader) {
return createClassLoaderContext((ClassLoader) resource,
logicalName);
}
return ResourceContext.NONE;
}
}

View File

@ -25,7 +25,7 @@
package com.sun.jmx.mbeanserver;
import javax.management.DynamicMBean;
import javax.management.DynamicWrapperMBean;
import javax.management.MBeanServer;
import javax.management.ObjectName;
@ -35,17 +35,7 @@ import javax.management.ObjectName;
*
* @since 1.6
*/
public interface DynamicMBean2 extends DynamicMBean {
/**
* The resource corresponding to this MBean. This is the object whose
* class name should be reflected by the MBean's
* getMBeanInfo().getClassName() for example. For a "plain"
* DynamicMBean it will be "this". For an MBean that wraps another
* object, like javax.management.StandardMBean, it will be the wrapped
* object.
*/
public Object getResource();
public interface DynamicMBean2 extends DynamicWrapperMBean {
/**
* The name of this MBean's class, as used by permission checks.
* This is typically equal to getResource().getClass().getName().

View File

@ -25,23 +25,39 @@
package com.sun.jmx.mbeanserver;
import com.sun.jmx.remote.util.EnvHelp;
import java.beans.BeanInfo;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import javax.management.AttributeNotFoundException;
import javax.management.Description;
import javax.management.Descriptor;
import javax.management.DescriptorFields;
import javax.management.DescriptorKey;
import javax.management.DynamicMBean;
import javax.management.ImmutableDescriptor;
import javax.management.MBean;
import javax.management.MBeanInfo;
import javax.management.MXBean;
import javax.management.NotCompliantMBeanException;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.MXBeanMappingFactory;
import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.remote.util.EnvHelp;
import java.beans.BeanInfo;
import java.beans.PropertyDescriptor;
@ -133,8 +149,12 @@ public class Introspector {
}
}
public static void checkCompliance(Class mbeanClass)
throws NotCompliantMBeanException {
public static void checkCompliance(Class<?> mbeanClass)
throws NotCompliantMBeanException {
// Check that @Resource is used correctly (if it used).
MBeanInjector.validate(mbeanClass);
// Is DynamicMBean?
//
if (DynamicMBean.class.isAssignableFrom(mbeanClass))
@ -157,21 +177,39 @@ public class Introspector {
} catch (NotCompliantMBeanException e) {
mxbeanException = e;
}
// Is @MBean or @MXBean class?
// In fact we find @MBean or @MXBean as a hacky variant of
// getStandardMBeanInterface or getMXBeanInterface. If we get here
// then nothing worked.
final String msg =
"MBean class " + mbeanClass.getName() + " does not implement " +
"DynamicMBean, neither follows the Standard MBean conventions (" +
mbeanException.toString() + ") nor the MXBean conventions (" +
mxbeanException.toString() + ")";
"DynamicMBean; does not follow the Standard MBean conventions (" +
mbeanException.toString() + "); does not follow the MXBean conventions (" +
mxbeanException.toString() + "); and does not have or inherit the @" +
MBean.class.getSimpleName() + " or @" + MXBean.class.getSimpleName() +
" annotation";
throw new NotCompliantMBeanException(msg);
}
/**
* <p>Make a DynamicMBean out of the existing MBean object. The object
* may already be a DynamicMBean, or it may be a Standard MBean or
* MXBean, possibly defined using {@code @MBean} or {@code @MXBean}.</p>
* @param mbean the object to convert to a DynamicMBean.
* @param <T> a type parameter defined for implementation convenience
* (which would have to be removed if this method were part of the public
* API).
* @return the converted DynamicMBean.
* @throws NotCompliantMBeanException if {@code mbean} is not a compliant
* MBean object, including the case where it is null.
*/
public static <T> DynamicMBean makeDynamicMBean(T mbean)
throws NotCompliantMBeanException {
if (mbean == null)
throw new NotCompliantMBeanException("Null MBean object");
if (mbean instanceof DynamicMBean)
return (DynamicMBean) mbean;
final Class mbeanClass = mbean.getClass();
final Class<?> mbeanClass = mbean.getClass();
Class<? super T> c = null;
try {
c = Util.cast(getStandardMBeanInterface(mbeanClass));
@ -270,7 +308,7 @@ public class Introspector {
* Return <code>null</code> if the MBean is a DynamicMBean,
* or if no MBean interface is found.
*/
public static Class getMBeanInterface(Class baseClass) {
public static Class<?> getMBeanInterface(Class<?> baseClass) {
// Check if the given class implements the MBean interface
// or the Dynamic MBean interface
if (isDynamic(baseClass)) return null;
@ -291,10 +329,12 @@ public class Introspector {
* @throws NotCompliantMBeanException The specified class is
* not a JMX compliant Standard MBean.
*/
public static Class getStandardMBeanInterface(Class baseClass)
throws NotCompliantMBeanException {
Class current = baseClass;
Class mbeanInterface = null;
public static <T> Class<? super T> getStandardMBeanInterface(Class<T> baseClass)
throws NotCompliantMBeanException {
if (baseClass.isAnnotationPresent(MBean.class))
return baseClass;
Class<? super T> current = baseClass;
Class<? super T> mbeanInterface = null;
while (current != null) {
mbeanInterface =
findMBeanInterface(current, current.getName());
@ -321,8 +361,10 @@ public class Introspector {
* @throws NotCompliantMBeanException The specified class is
* not a JMX compliant MXBean.
*/
public static Class getMXBeanInterface(Class baseClass)
public static <T> Class<? super T> getMXBeanInterface(Class<T> baseClass)
throws NotCompliantMBeanException {
if (hasMXBeanAnnotation(baseClass))
return baseClass;
try {
return MXBeanSupport.findMXBeanInterface(baseClass);
} catch (Exception e) {
@ -345,19 +387,24 @@ public class Introspector {
* ------------------------------------------
*/
static boolean hasMXBeanAnnotation(Class<?> c) {
MXBean m = c.getAnnotation(MXBean.class);
return (m != null && m.value());
}
/**
* Try to find the MBean interface corresponding to the class aName
* - i.e. <i>aName</i>MBean, from within aClass and its superclasses.
**/
private static Class findMBeanInterface(Class aClass, String aName) {
Class current = aClass;
private static <T> Class<? super T> findMBeanInterface(
Class<T> aClass, String aName) {
Class<? super T> current = aClass;
while (current != null) {
final Class[] interfaces = current.getInterfaces();
final Class<?>[] interfaces = current.getInterfaces();
final int len = interfaces.length;
for (int i=0;i<len;i++) {
final Class inter =
implementsMBean(interfaces[i], aName);
Class<? super T> inter = Util.cast(interfaces[i]);
inter = implementsMBean(inter, aName);
if (inter != null) return inter;
}
current = current.getSuperclass();
@ -365,6 +412,48 @@ public class Introspector {
return null;
}
public static String descriptionForElement(AnnotatedElement elmt) {
if (elmt == null)
return null;
Description d = elmt.getAnnotation(Description.class);
if (d == null)
return null;
return d.value();
}
public static String descriptionForParameter(
Annotation[] parameterAnnotations) {
for (Annotation a : parameterAnnotations) {
if (a instanceof Description)
return ((Description) a).value();
}
return null;
}
public static String nameForParameter(
Annotation[] parameterAnnotations) {
for (Annotation a : parameterAnnotations) {
Class<? extends Annotation> ac = a.annotationType();
// You'd really have to go out of your way to have more than
// one @Name annotation, so we don't check for that.
if (ac.getSimpleName().equals("Name")) {
try {
Method value = ac.getMethod("value");
if (value.getReturnType() == String.class &&
value.getParameterTypes().length == 0) {
return (String) value.invoke(a);
}
} catch (Exception e) {
MBEANSERVER_LOGGER.log(
Level.WARNING,
"Unexpected exception getting @" + ac.getName(),
e);
}
}
}
return null;
}
public static Descriptor descriptorForElement(final AnnotatedElement elmt) {
if (elmt == null)
return ImmutableDescriptor.EMPTY_DESCRIPTOR;
@ -372,41 +461,18 @@ public class Introspector {
return descriptorForAnnotations(annots);
}
public static Descriptor descriptorForAnnotation(Annotation annot) {
return descriptorForAnnotations(new Annotation[] {annot});
}
public static Descriptor descriptorForAnnotations(Annotation[] annots) {
if (annots.length == 0)
return ImmutableDescriptor.EMPTY_DESCRIPTOR;
Map<String, Object> descriptorMap = new HashMap<String, Object>();
for (Annotation a : annots) {
Class<? extends Annotation> c = a.annotationType();
Method[] elements = c.getMethods();
for (Method element : elements) {
DescriptorKey key = element.getAnnotation(DescriptorKey.class);
if (key != null) {
String name = key.value();
Object value;
try {
value = element.invoke(a);
} catch (RuntimeException e) {
// we don't expect this - except for possibly
// security exceptions?
// RuntimeExceptions shouldn't be "UndeclaredThrowable".
// anyway...
//
throw e;
} catch (Exception e) {
// we don't expect this
throw new UndeclaredThrowableException(e);
}
value = annotationToField(value);
Object oldValue = descriptorMap.put(name, value);
if (oldValue != null && !equals(oldValue, value)) {
final String msg =
"Inconsistent values for descriptor field " + name +
" from annotations: " + value + " :: " + oldValue;
throw new IllegalArgumentException(msg);
}
}
}
if (a instanceof DescriptorFields)
addDescriptorFieldsToMap(descriptorMap, (DescriptorFields) a);
addAnnotationFieldsToMap(descriptorMap, a);
}
if (descriptorMap.isEmpty())
@ -415,6 +481,62 @@ public class Introspector {
return new ImmutableDescriptor(descriptorMap);
}
private static void addDescriptorFieldsToMap(
Map<String, Object> descriptorMap, DescriptorFields df) {
for (String field : df.value()) {
int eq = field.indexOf('=');
if (eq < 0) {
throw new IllegalArgumentException(
"@DescriptorFields string must contain '=': " +
field);
}
String name = field.substring(0, eq);
String value = field.substring(eq + 1);
addToMap(descriptorMap, name, value);
}
}
private static void addAnnotationFieldsToMap(
Map<String, Object> descriptorMap, Annotation a) {
Class<? extends Annotation> c = a.annotationType();
Method[] elements = c.getMethods();
for (Method element : elements) {
DescriptorKey key = element.getAnnotation(DescriptorKey.class);
if (key != null) {
String name = key.value();
Object value;
try {
value = element.invoke(a);
} catch (RuntimeException e) {
// we don't expect this - except for possibly
// security exceptions?
// RuntimeExceptions shouldn't be "UndeclaredThrowable".
// anyway...
throw e;
} catch (Exception e) {
// we don't expect this
throw new UndeclaredThrowableException(e);
}
if (!key.omitIfDefault() ||
!equals(value, element.getDefaultValue())) {
value = annotationToField(value);
addToMap(descriptorMap, name, value);
}
}
}
}
private static void addToMap(
Map<String, Object> descriptorMap, String name, Object value) {
Object oldValue = descriptorMap.put(name, value);
if (oldValue != null && !equals(oldValue, value)) {
final String msg =
"Inconsistent values for descriptor field " + name +
" from annotations: " + value + " :: " + oldValue;
throw new IllegalArgumentException(msg);
}
}
/**
* Throws a NotCompliantMBeanException or a SecurityException.
* @param notCompliant the class which was under examination
@ -473,8 +595,13 @@ public class Introspector {
// The only other possibility is that the value is another
// annotation, or that the language has evolved since this code
// was written. We don't allow for either of those currently.
// If it is indeed another annotation, then x will be a proxy
// with an unhelpful name like $Proxy2. So we extract the
// proxy's interface to use that in the exception message.
if (Proxy.isProxyClass(c))
c = c.getInterfaces()[0]; // array "can't be empty"
throw new IllegalArgumentException("Illegal type for annotation " +
"element: " + x.getClass().getName());
"element using @DescriptorKey: " + c.getName());
}
// This must be consistent with the check for duplicate field values in
@ -490,15 +617,15 @@ public class Introspector {
* @param c The interface to be tested
* @param clName The name of the class implementing this interface
*/
private static Class implementsMBean(Class c, String clName) {
private static <T> Class<? super T> implementsMBean(Class<T> c, String clName) {
String clMBeanName = clName + "MBean";
if (c.getName().equals(clMBeanName)) {
return c;
}
Class[] interfaces = c.getInterfaces();
Class<?>[] interfaces = c.getInterfaces();
for (int i = 0;i < interfaces.length; i++) {
if (interfaces[i].getName().equals(clMBeanName))
return interfaces[i];
return Util.cast(interfaces[i]);
}
return null;

View File

@ -33,6 +33,10 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.MBean;
import javax.management.MXBean;
import javax.management.ManagedAttribute;
import javax.management.ManagedOperation;
import javax.management.NotCompliantMBeanException;
/**
@ -125,18 +129,26 @@ class MBeanAnalyzer<M> {
for (Method m : methods) {
final String name = m.getName();
final int nParams = m.getParameterTypes().length;
final boolean managedOp = m.isAnnotationPresent(ManagedOperation.class);
final boolean managedAttr = m.isAnnotationPresent(ManagedAttribute.class);
if (managedOp && managedAttr) {
throw new NotCompliantMBeanException("Method " + name +
" has both @ManagedOperation and @ManagedAttribute");
}
final M cm = introspector.mFrom(m);
String attrName = "";
if (name.startsWith("get"))
attrName = name.substring(3);
else if (name.startsWith("is")
&& m.getReturnType() == boolean.class)
attrName = name.substring(2);
if (!managedOp) {
if (name.startsWith("get"))
attrName = name.substring(3);
else if (name.startsWith("is")
&& m.getReturnType() == boolean.class)
attrName = name.substring(2);
}
if (attrName.length() != 0 && nParams == 0
&& m.getReturnType() != void.class) {
&& m.getReturnType() != void.class && !managedOp) {
// It's a getter
// Check we don't have both isX and getX
AttrMethods<M> am = attrMap.get(attrName);
@ -153,7 +165,7 @@ class MBeanAnalyzer<M> {
attrMap.put(attrName, am);
} else if (name.startsWith("set") && name.length() > 3
&& nParams == 1 &&
m.getReturnType() == void.class) {
m.getReturnType() == void.class && !managedOp) {
// It's a setter
attrName = name.substring(3);
AttrMethods<M> am = attrMap.get(attrName);
@ -166,6 +178,9 @@ class MBeanAnalyzer<M> {
}
am.setter = cm;
attrMap.put(attrName, am);
} else if (managedAttr) {
throw new NotCompliantMBeanException("Method " + name +
" has @ManagedAttribute but is not a valid getter or setter");
} else {
// It's an operation
List<M> cms = opMap.get(name);

View File

@ -0,0 +1,291 @@
/*
* 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 com.sun.jmx.mbeanserver;
import java.lang.ref.WeakReference;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.WeakHashMap;
import javax.annotation.Resource;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import static com.sun.jmx.mbeanserver.Util.newMap;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.management.SendNotification;
public class MBeanInjector {
private static Class<?>[] injectedClasses = {
MBeanServer.class, ObjectName.class, SendNotification.class,
};
public static void inject(Object mbean, MBeanServer mbs, ObjectName name)
throws Exception {
ClassInjector injector = injectorForClass(mbean.getClass());
injector.inject(mbean, MBeanServer.class, mbs);
injector.inject(mbean, ObjectName.class, name);
}
public static boolean injectsSendNotification(Object mbean)
throws NotCompliantMBeanException {
ClassInjector injector = injectorForClass(mbean.getClass());
return injector.injects(SendNotification.class);
}
public static void injectSendNotification(Object mbean, SendNotification sn)
throws Exception {
ClassInjector injector = injectorForClass(mbean.getClass());
injector.inject(mbean, SendNotification.class, sn);
}
public static void validate(Class<?> c) throws NotCompliantMBeanException {
injectorForClass(c);
}
private static class ClassInjector {
private Map<Class<?>, List<Field>> fields;
private Map<Class<?>, List<Method>> methods;
ClassInjector(Class<?> c) throws NotCompliantMBeanException {
fields = newMap();
methods = newMap();
Class<?> sup = c.getSuperclass();
ClassInjector supInjector;
if (sup == null) {
supInjector = null;
} else {
supInjector = injectorForClass(sup);
fields.putAll(supInjector.fields);
methods.putAll(supInjector.methods);
}
addMembers(c);
eliminateOverriddenMethods();
// If we haven't added any new fields or methods to what we
// inherited, then we can share the parent's maps.
if (supInjector != null) {
if (fields.equals(supInjector.fields))
fields = supInjector.fields;
if (methods.equals(supInjector.methods))
methods = supInjector.methods;
}
}
boolean injects(Class<?> c) {
return (fields.get(c) != null || methods.get(c) != null);
}
<T> void inject(Object instance, Class<T> type, T resource)
throws Exception {
List<Field> fs = fields.get(type);
if (fs != null) {
for (Field f : fs)
f.set(instance, resource);
}
List<Method> ms = methods.get(type);
if (ms != null) {
for (Method m : ms) {
try {
m.invoke(instance, resource);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof Error)
throw (Error) cause;
else
throw (Exception) cause;
}
}
}
}
private void eliminateOverriddenMethods() {
/* Covariant overriding is unlikely, but it is possible that the
* parent has a @Resource method that we override with another
* @Resource method. We don't want to invoke both methods,
* because polymorphism means we would actually invoke the same
* method twice.
*/
for (Map.Entry<Class<?>, List<Method>> entry : methods.entrySet()) {
List<Method> list = entry.getValue();
list = MBeanAnalyzer.eliminateCovariantMethods(list);
entry.setValue(list);
}
}
/*
* Find Fields or Methods within the given Class that we can inject
* resource references into. Suppose we want to know if a Field can get
* a reference to an ObjectName. We'll accept fields like this:
*
* @Resource
* private transient ObjectName name;
*
* or like this:
*
* @Resource(type = ObjectName.class)
* private transient Object name;
*
* but not like this:
*
* @Resource
* private transient Object name;
*
* (Plain @Resource is equivalent to @Resource(type = Object.class).)
*
* We don't want to inject into everything that might possibly accept
* an ObjectName reference, because examples like the last one above
* could also accept an MBeanServer reference or any other sort of
* reference.
*
* So we accept a Field if it has a @Resource annotation and either
* (a) its type is ObjectName or a subclass and its @Resource type is
* compatible with ObjectName (e.g. it is Object); or
* (b) its type is compatible with ObjectName and its @Resource type
* is exactly ObjectName. Fields that meet these criteria will not
* meet the same criteria with respect to other types such as MBeanServer.
*
* The same logic applies mutatis mutandis to Methods such as this:
*
* @Resource
* private void setObjectName1(ObjectName name)
* @Resource(type = Object.class)
* private void setObjectName2(Object name)
*/
private void addMembers(final Class<?> c)
throws NotCompliantMBeanException {
AccessibleObject[][] memberArrays =
AccessController.doPrivileged(
new PrivilegedAction<AccessibleObject[][]>() {
public AccessibleObject[][] run() {
return new AccessibleObject[][] {
c.getDeclaredFields(), c.getDeclaredMethods()
};
}
});
for (AccessibleObject[] members : memberArrays) {
for (final AccessibleObject member : members) {
Resource res = member.getAnnotation(Resource.class);
if (res == null)
continue;
final Field field;
final Method method;
final Class<?> memberType;
final int modifiers;
if (member instanceof Field) {
field = (Field) member;
memberType = field.getType();
modifiers = field.getModifiers();
method = null;
} else {
field = null;
method = (Method) member;
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 1) {
throw new NotCompliantMBeanException(
"@Resource method must have exactly 1 " +
"parameter: " + method);
}
if (method.getReturnType() != void.class) {
throw new NotCompliantMBeanException(
"@Resource method must return void: " +
method);
}
memberType = paramTypes[0];
modifiers = method.getModifiers();
}
if (Modifier.isStatic(modifiers)) {
throw new NotCompliantMBeanException(
"@Resource method or field cannot be static: " +
member);
}
for (Class<?> injectedClass : injectedClasses) {
Class<?>[] types = {memberType, res.type()};
boolean accept = false;
for (int i = 0; i < 2; i++) {
if (types[i] == injectedClass &&
types[1 - i].isAssignableFrom(injectedClass)) {
accept = true;
break;
}
}
if (accept) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
member.setAccessible(true);
return null;
}
});
addToMap(fields, injectedClass, field);
addToMap(methods, injectedClass, method);
}
}
}
}
}
private static <K, V> void addToMap(Map<K, List<V>> map, K key, V value) {
if (value == null)
return;
List<V> list = map.get(key);
if (list == null)
list = Collections.singletonList(value);
else {
if (list.size() == 1)
list = new ArrayList<V>(list);
list.add(value);
}
map.put(key, list);
}
}
private static synchronized ClassInjector injectorForClass(Class<?> c)
throws NotCompliantMBeanException {
WeakReference<ClassInjector> wr = injectorMap.get(c);
ClassInjector ci = (wr == null) ? null : wr.get();
if (ci == null) {
ci = new ClassInjector(c);
injectorMap.put(c, new WeakReference<ClassInjector>(ci));
}
return ci;
}
private static Map<Class<?>, WeakReference<ClassInjector>> injectorMap =
new WeakHashMap<Class<?>, WeakReference<ClassInjector>>();
}

View File

@ -36,20 +36,28 @@ import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import javax.management.Description;
import javax.management.Descriptor;
import javax.management.ImmutableDescriptor;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.MBean;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MXBean;
import javax.management.ManagedAttribute;
import javax.management.ManagedOperation;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationInfo;
import javax.management.NotificationInfos;
import javax.management.ReflectionException;
/**
@ -153,6 +161,25 @@ abstract class MBeanIntrospector<M> {
abstract MBeanAttributeInfo getMBeanAttributeInfo(String attributeName,
M getter, M setter) throws IntrospectionException;
final String getAttributeDescription(
String attributeName, String defaultDescription,
Method getter, Method setter) throws IntrospectionException {
String g = Introspector.descriptionForElement(getter);
String s = Introspector.descriptionForElement(setter);
if (g == null) {
if (s == null)
return defaultDescription;
else
return s;
} else if (s == null || g.equals(s)) {
return g;
} else {
throw new IntrospectionException(
"Inconsistent @Description on getter and setter for " +
"attribute " + attributeName);
}
}
/**
* Construct an MBeanOperationInfo for the given operation based on
* the M it was derived from.
@ -184,8 +211,12 @@ abstract class MBeanIntrospector<M> {
}
void checkCompliance(Class<?> mbeanType) throws NotCompliantMBeanException {
if (!mbeanType.isInterface()) {
throw new NotCompliantMBeanException("Not an interface: " +
if (!mbeanType.isInterface() &&
!mbeanType.isAnnotationPresent(MBean.class) &&
!Introspector.hasMXBeanAnnotation(mbeanType)) {
throw new NotCompliantMBeanException("Not an interface and " +
"does not have @" + MBean.class.getSimpleName() +
" or @" + MXBean.class.getSimpleName() + " annotation: " +
mbeanType.getName());
}
}
@ -194,7 +225,12 @@ abstract class MBeanIntrospector<M> {
* Get the methods to be analyzed to build the MBean interface.
*/
List<Method> getMethods(final Class<?> mbeanType) throws Exception {
return Arrays.asList(mbeanType.getMethods());
if (mbeanType.isInterface())
return Arrays.asList(mbeanType.getMethods());
final List<Method> methods = newList();
getAnnotatedMethods(mbeanType, methods);
return methods;
}
final PerInterface<M> getPerInterface(Class<?> mbeanInterface)
@ -232,8 +268,11 @@ abstract class MBeanIntrospector<M> {
MBeanAnalyzer<M> analyzer) throws IntrospectionException {
final MBeanInfoMaker maker = new MBeanInfoMaker();
analyzer.visit(maker);
final String description =
final String defaultDescription =
"Information on the management interface of the MBean";
String description = Introspector.descriptionForElement(mbeanInterface);
if (description == null)
description = defaultDescription;
return maker.makeMBeanInfo(mbeanInterface, description);
}
@ -407,7 +446,15 @@ abstract class MBeanIntrospector<M> {
throws NotCompliantMBeanException {
MBeanInfo mbi =
getClassMBeanInfo(resource.getClass(), perInterface);
MBeanNotificationInfo[] notifs = findNotifications(resource);
MBeanNotificationInfo[] notifs;
try {
notifs = findNotifications(resource);
} catch (RuntimeException e) {
NotCompliantMBeanException x =
new NotCompliantMBeanException(e.getMessage());
x.initCause(e);
throw x;
}
Descriptor d = getSpecificMBeanDescriptor();
boolean anyNotifs = (notifs != null && notifs.length > 0);
if (!anyNotifs && ImmutableDescriptor.EMPTY_DESCRIPTOR.equals(d))
@ -460,13 +507,43 @@ abstract class MBeanIntrospector<M> {
}
}
/*
* Add to "methods" every public method that has the @ManagedAttribute
* or @ManagedOperation annotation, in the given class or any of
* its superclasses or superinterfaces.
*
* We always add superclass or superinterface methods first, so that
* the stable sort used by eliminateCovariantMethods will put the
* method from the most-derived class last. This means that we will
* see the version of the @ManagedAttribute (or ...Operation) annotation
* from that method, which might have a different description or whatever.
*/
private static void getAnnotatedMethods(Class<?> c, List<Method> methods)
throws Exception {
Class<?> sup = c.getSuperclass();
if (sup != null)
getAnnotatedMethods(sup, methods);
Class<?>[] intfs = c.getInterfaces();
for (Class<?> intf : intfs)
getAnnotatedMethods(intf, methods);
for (Method m : c.getMethods()) {
// We are careful not to add m if it is inherited from a parent
// class or interface, because duplicate methods lead to nasty
// behaviour in eliminateCovariantMethods.
if (m.getDeclaringClass() == c &&
(m.isAnnotationPresent(ManagedAttribute.class) ||
m.isAnnotationPresent(ManagedOperation.class)))
methods.add(m);
}
}
static MBeanNotificationInfo[] findNotifications(Object moi) {
if (!(moi instanceof NotificationBroadcaster))
return null;
MBeanNotificationInfo[] mbn =
((NotificationBroadcaster) moi).getNotificationInfo();
if (mbn == null || mbn.length == 0)
return null;
return findNotificationsFromAnnotations(moi.getClass());
MBeanNotificationInfo[] result =
new MBeanNotificationInfo[mbn.length];
for (int i = 0; i < mbn.length; i++) {
@ -478,11 +555,81 @@ abstract class MBeanIntrospector<M> {
return result;
}
private static MBeanNotificationInfo[] findNotificationsFromAnnotations(
Class<?> mbeanClass) {
Class<?> c = getAnnotatedNotificationInfoClass(mbeanClass);
if (c == null)
return null;
NotificationInfo ni = c.getAnnotation(NotificationInfo.class);
NotificationInfos nis = c.getAnnotation(NotificationInfos.class);
List<NotificationInfo> list = newList();
if (ni != null)
list.add(ni);
if (nis != null)
list.addAll(Arrays.asList(nis.value()));
if (list.isEmpty())
return null;
List<MBeanNotificationInfo> mbnis = newList();
for (NotificationInfo x : list) {
// The Descriptor includes any fields explicitly specified by
// x.descriptorFields(), plus any fields from the contained
// @Description annotation.
Descriptor d = new ImmutableDescriptor(x.descriptorFields());
d = ImmutableDescriptor.union(
d, Introspector.descriptorForAnnotation(x.description()));
MBeanNotificationInfo mbni = new MBeanNotificationInfo(
x.types(), x.notificationClass().getName(),
x.description().value(), d);
mbnis.add(mbni);
}
return mbnis.toArray(new MBeanNotificationInfo[mbnis.size()]);
}
private static final Map<Class<?>, WeakReference<Class<?>>>
annotatedNotificationInfoClasses = newWeakHashMap();
private static Class<?> getAnnotatedNotificationInfoClass(Class<?> baseClass) {
synchronized (annotatedNotificationInfoClasses) {
WeakReference<Class<?>> wr =
annotatedNotificationInfoClasses.get(baseClass);
if (wr != null)
return wr.get();
Class<?> c = null;
if (baseClass.isAnnotationPresent(NotificationInfo.class) ||
baseClass.isAnnotationPresent(NotificationInfos.class)) {
c = baseClass;
} else {
Class<?>[] intfs = baseClass.getInterfaces();
for (Class<?> intf : intfs) {
Class<?> c1 = getAnnotatedNotificationInfoClass(intf);
if (c1 != null) {
if (c != null) {
throw new IllegalArgumentException(
"Class " + baseClass.getName() + " inherits " +
"@NotificationInfo(s) from both " +
c.getName() + " and " + c1.getName());
}
c = c1;
}
}
}
// Record the result of the search. If no @NotificationInfo(s)
// were found, c is null, and we store a WeakReference(null).
// This prevents us from having to search again and fail again.
annotatedNotificationInfoClasses.put(baseClass,
new WeakReference<Class<?>>(c));
return c;
}
}
private static MBeanConstructorInfo[] findConstructors(Class<?> c) {
Constructor[] cons = c.getConstructors();
MBeanConstructorInfo[] mbc = new MBeanConstructorInfo[cons.length];
for (int i = 0; i < cons.length; i++) {
final String descr = "Public constructor of the MBean";
String descr = "Public constructor of the MBean";
Description d = cons[i].getAnnotation(Description.class);
if (d != null)
descr = d.value();
mbc[i] = new MBeanConstructorInfo(descr, cons[i]);
}
return mbc;

View File

@ -263,10 +263,14 @@ public abstract class MBeanSupport<M>
return resource.getClass().getName();
}
public final Object getResource() {
public final Object getWrappedObject() {
return resource;
}
public final ClassLoader getWrappedClassLoader() {
return resource.getClass().getClassLoader();
}
public final Class<?> getMBeanInterface() {
return perInterface.getMBeanInterface();
}

View File

@ -35,6 +35,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.WeakHashMap;
import javax.management.Description;
import javax.management.Descriptor;
import javax.management.ImmutableDescriptor;
import javax.management.IntrospectionException;
@ -43,6 +44,7 @@ import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ManagedOperation;
import javax.management.NotCompliantMBeanException;
import javax.management.openmbean.MXBeanMappingFactory;
import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
@ -180,7 +182,10 @@ class MXBeanIntrospector extends MBeanIntrospector<ConvertingMethod> {
final boolean isWritable = (setter != null);
final boolean isIs = isReadable && getName(getter).startsWith("is");
final String description = attributeName;
final String description = getAttributeDescription(
attributeName, attributeName,
getter == null ? null : getter.getMethod(),
setter == null ? null : setter.getMethod());
final OpenType<?> openType;
final Type originalType;
@ -229,13 +234,17 @@ class MXBeanIntrospector extends MBeanIntrospector<ConvertingMethod> {
MBeanOperationInfo getMBeanOperationInfo(String operationName,
ConvertingMethod operation) {
final Method method = operation.getMethod();
final String description = operationName;
String description = operationName;
/* Ideally this would be an empty string, but
OMBOperationInfo constructor forbids that. Also, we
could consult an annotation to get a useful
description. */
OMBOperationInfo constructor forbids that. */
Description d = method.getAnnotation(Description.class);
if (d != null)
description = d.value();
final int impact = MBeanOperationInfo.UNKNOWN;
int impact = MBeanOperationInfo.UNKNOWN;
ManagedOperation annot = method.getAnnotation(ManagedOperation.class);
if (annot != null)
impact = annot.impact().getCode();
final OpenType<?> returnType = operation.getOpenReturnType();
final Type originalReturnType = operation.getGenericReturnType();
@ -247,8 +256,15 @@ class MXBeanIntrospector extends MBeanIntrospector<ConvertingMethod> {
boolean openParameterTypes = true;
Annotation[][] annots = method.getParameterAnnotations();
for (int i = 0; i < paramTypes.length; i++) {
final String paramName = "p" + i;
final String paramDescription = paramName;
String paramName = Introspector.nameForParameter(annots[i]);
if (paramName == null)
paramName = "p" + i;
String paramDescription =
Introspector.descriptionForParameter(annots[i]);
if (paramDescription == null)
paramDescription = paramName;
final OpenType<?> openType = paramTypes[i];
final Type originalType = originalParamTypes[i];
Descriptor descriptor =

View File

@ -161,7 +161,7 @@ public class MXBeanSupport extends MBeanSupport<ConvertingMethod> {
synchronized (lock) {
this.mxbeanLookup = MXBeanLookup.Plain.lookupFor(server);
this.mxbeanLookup.addReference(name, getResource());
this.mxbeanLookup.addReference(name, getWrappedObject());
this.objectName = name;
}
}
@ -170,7 +170,7 @@ public class MXBeanSupport extends MBeanSupport<ConvertingMethod> {
public void unregister() {
synchronized (lock) {
if (mxbeanLookup != null) {
if (mxbeanLookup.removeReference(objectName, getResource()))
if (mxbeanLookup.removeReference(objectName, getWrappedObject()))
objectName = null;
}
// XXX: need to revisit the whole register/unregister logic in

View File

@ -0,0 +1,186 @@
/*
* 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 com.sun.jmx.mbeanserver;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.DynamicWrapperMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;
/**
* Create wrappers for DynamicMBean that implement NotificationEmitter
* and SendNotification.
*/
public class NotifySupport
implements DynamicMBean2, NotificationEmitter, MBeanRegistration {
private final DynamicMBean mbean;
private final NotificationBroadcasterSupport nbs;
public static DynamicMBean wrap(
DynamicMBean mbean, NotificationBroadcasterSupport nbs) {
return new NotifySupport(mbean, nbs);
}
private NotifySupport(DynamicMBean mbean, NotificationBroadcasterSupport nbs) {
this.mbean = mbean;
this.nbs = nbs;
}
public static NotificationBroadcasterSupport getNB(DynamicMBean mbean) {
if (mbean instanceof NotifySupport)
return ((NotifySupport) mbean).nbs;
else
return null;
}
public String getClassName() {
if (mbean instanceof DynamicMBean2)
return ((DynamicMBean2) mbean).getClassName();
Object w = mbean;
if (w instanceof DynamicWrapperMBean)
w = ((DynamicWrapperMBean) w).getWrappedObject();
return w.getClass().getName();
}
public void preRegister2(MBeanServer mbs, ObjectName name) throws Exception {
if (mbean instanceof DynamicMBean2)
((DynamicMBean2) mbean).preRegister2(mbs, name);
}
public void registerFailed() {
if (mbean instanceof DynamicMBean2)
((DynamicMBean2) mbean).registerFailed();
}
public Object getWrappedObject() {
if (mbean instanceof DynamicWrapperMBean)
return ((DynamicWrapperMBean) mbean).getWrappedObject();
else
return mbean;
}
public ClassLoader getWrappedClassLoader() {
if (mbean instanceof DynamicWrapperMBean)
return ((DynamicWrapperMBean) mbean).getWrappedClassLoader();
else
return mbean.getClass().getClassLoader();
}
public Object getAttribute(String attribute) throws AttributeNotFoundException,
MBeanException,
ReflectionException {
return mbean.getAttribute(attribute);
}
public void setAttribute(Attribute attribute) throws AttributeNotFoundException,
InvalidAttributeValueException,
MBeanException,
ReflectionException {
mbean.setAttribute(attribute);
}
public AttributeList setAttributes(AttributeList attributes) {
return mbean.setAttributes(attributes);
}
public Object invoke(String actionName, Object[] params, String[] signature)
throws MBeanException, ReflectionException {
return mbean.invoke(actionName, params, signature);
}
public MBeanInfo getMBeanInfo() {
return mbean.getMBeanInfo();
}
public AttributeList getAttributes(String[] attributes) {
return mbean.getAttributes(attributes);
}
public void removeNotificationListener(NotificationListener listener,
NotificationFilter filter,
Object handback) throws ListenerNotFoundException {
nbs.removeNotificationListener(listener, filter, handback);
}
public void removeNotificationListener(NotificationListener listener)
throws ListenerNotFoundException {
nbs.removeNotificationListener(listener);
}
public MBeanNotificationInfo[] getNotificationInfo() {
return nbs.getNotificationInfo();
}
public void addNotificationListener(NotificationListener listener,
NotificationFilter filter,
Object handback) {
nbs.addNotificationListener(listener, filter, handback);
}
public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
if (mbr() != null)
return mbr().preRegister(server, name);
else
return name;
}
public void postRegister(Boolean registrationDone) {
if (mbr() != null)
mbr().postRegister(registrationDone);
}
public void preDeregister() throws Exception {
if (mbr() != null)
mbr().preDeregister();
}
public void postDeregister() {
if (mbr() != null)
mbr().postDeregister();
}
private MBeanRegistration mbr() {
if (mbean instanceof MBeanRegistration)
return (MBeanRegistration) mbean;
else
return null;
}
}

View File

@ -29,6 +29,7 @@ import com.sun.jmx.defaults.ServiceName;
import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -39,7 +40,6 @@ import java.util.Set;
import javax.management.DynamicMBean;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.RuntimeOperationsException;
@ -52,6 +52,27 @@ import javax.management.RuntimeOperationsException;
*/
public class Repository {
/**
* An interface that allows the caller to get some control
* over the registration.
* @see #addMBean
* @see #remove
*/
public interface RegistrationContext {
/**
* Called by {@link #addMBean}.
* Can throw a RuntimeOperationsException to cancel the
* registration.
*/
public void registering();
/**
* Called by {@link #remove}.
* Any exception thrown by this method will be ignored.
*/
public void unregistered();
}
// Private fields -------------------------------------------->
/**
@ -115,7 +136,6 @@ public class Repository {
/**
* Builds a new ObjectNamePattern object from an ObjectName pattern
* constituents.
* @param domain pattern.getDomain().
* @param propertyListPattern pattern.isPropertyListPattern().
* @param propertyValuePattern pattern.isPropertyValuePattern().
* @param canonicalProps pattern.getCanonicalKeyPropertyListString().
@ -216,16 +236,6 @@ public class Repository {
}
}
private void addNewDomMoi(final DynamicMBean object, final String dom,
final ObjectName name) {
final Map<String,NamedObject> moiTb =
new HashMap<String,NamedObject>();
moiTb.put(name.getCanonicalKeyPropertyListString(),
new NamedObject(name, object));
domainTb.put(dom, moiTb);
nbElements++;
}
/** Match a string against a shell-style pattern. The only pattern
characters recognised are <code>?</code>, standing for any one
character, and <code>*</code>, standing for any string of
@ -306,6 +316,50 @@ public class Repository {
}
}
private void addNewDomMoi(final DynamicMBean object,
final String dom,
final ObjectName name,
final RegistrationContext context) {
final Map<String,NamedObject> moiTb =
new HashMap<String,NamedObject>();
final String key = name.getCanonicalKeyPropertyListString();
addMoiToTb(object,name,key,moiTb,context);
domainTb.put(dom, moiTb);
nbElements++;
}
private void registering(RegistrationContext context) {
if (context == null) return;
try {
context.registering();
} catch (RuntimeOperationsException x) {
throw x;
} catch (RuntimeException x) {
throw new RuntimeOperationsException(x);
}
}
private void unregistering(RegistrationContext context, ObjectName name) {
if (context == null) return;
try {
context.unregistered();
} catch (Exception x) {
// shouldn't come here...
MBEANSERVER_LOGGER.log(Level.FINE,
"Unexpected exception while unregistering "+name,
x);
}
}
private void addMoiToTb(final DynamicMBean object,
final ObjectName name,
final String key,
final Map<String,NamedObject> moiTb,
final RegistrationContext context) {
registering(context);
moiTb.put(key,new NamedObject(name, object));
}
/**
* Retrieves the named object contained in repository
* from the given objectname.
@ -355,12 +409,12 @@ public class Repository {
domainTb = new HashMap<String,Map<String,NamedObject>>(5);
if (domain != null && domain.length() != 0)
this.domain = domain;
this.domain = domain.intern(); // we use == domain later on...
else
this.domain = ServiceName.DOMAIN;
// Creates an new hastable for the default domain
domainTb.put(this.domain.intern(), new HashMap<String,NamedObject>());
// Creates a new hashtable for the default domain
domainTb.put(this.domain, new HashMap<String,NamedObject>());
}
/**
@ -395,10 +449,21 @@ public class Repository {
/**
* Stores an MBean associated with its object name in the repository.
*
* @param object MBean to be stored in the repository.
* @param name MBean object name.
* @param object MBean to be stored in the repository.
* @param name MBean object name.
* @param context A registration context. If non null, the repository
* will call {@link RegistrationContext#registering()
* context.registering()} from within the repository
* lock, when it has determined that the {@code object}
* can be stored in the repository with that {@code name}.
* If {@link RegistrationContext#registering()
* context.registering()} throws an exception, the
* operation is abandonned, the MBean is not added to the
* repository, and a {@link RuntimeOperationsException}
* is thrown.
*/
public void addMBean(final DynamicMBean object, ObjectName name)
public void addMBean(final DynamicMBean object, ObjectName name,
final RegistrationContext context)
throws InstanceAlreadyExistsException {
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
@ -431,7 +496,7 @@ public class Repository {
lock.writeLock().lock();
try {
// Domain cannot be JMImplementation if entry does not exists
// Domain cannot be JMImplementation if entry does not exist
if ( !to_default_domain &&
dom.equals("JMImplementation") &&
domainTb.containsKey("JMImplementation")) {
@ -440,21 +505,21 @@ public class Repository {
"Repository: domain name cannot be JMImplementation"));
}
// If domain not already exists, add it to the hash table
// If domain does not already exist, add it to the hash table
final Map<String,NamedObject> moiTb = domainTb.get(dom);
if (moiTb == null) {
addNewDomMoi(object, dom, name);
addNewDomMoi(object, dom, name, context);
return;
}
// Add instance if not already present
String cstr = name.getCanonicalKeyPropertyListString();
NamedObject elmt= moiTb.get(cstr);
if (elmt != null) {
throw new InstanceAlreadyExistsException(name.toString());
} else {
nbElements++;
moiTb.put(cstr, new NamedObject(name, object));
// Add instance if not already present
String cstr = name.getCanonicalKeyPropertyListString();
NamedObject elmt= moiTb.get(cstr);
if (elmt != null) {
throw new InstanceAlreadyExistsException(name.toString());
} else {
nbElements++;
addMoiToTb(object,name,cstr,moiTb,context);
}
}
} finally {
@ -533,7 +598,7 @@ public class Repository {
// ":*", ":[key=value],*" : names in defaultDomain
// "domain:*", "domain:[key=value],*" : names in the specified domain
// Surely one of the most frequent case ... query on the whole world
// Surely one of the most frequent cases ... query on the whole world
ObjectName name;
if (pattern == null ||
pattern.getCanonicalName().length() == 0 ||
@ -546,8 +611,7 @@ public class Repository {
// If pattern is not a pattern, retrieve this mbean !
if (!name.isPattern()) {
final NamedObject no;
no = retrieveNamedObject(name);
final NamedObject no = retrieveNamedObject(name);
if (no != null) result.add(no);
return result;
}
@ -577,12 +641,22 @@ public class Repository {
return result;
}
if (!name.isDomainPattern()) {
final Map<String,NamedObject> moiTb = domainTb.get(name.getDomain());
if (moiTb == null) return Collections.emptySet();
if (allNames)
result.addAll(moiTb.values());
else
addAllMatching(moiTb, result, namePattern);
return result;
}
// Pattern matching in the domain name (*, ?)
char[] dom2Match = name.getDomain().toCharArray();
for (String domain : domainTb.keySet()) {
char[] theDom = domain.toCharArray();
for (String dom : domainTb.keySet()) {
char[] theDom = dom.toCharArray();
if (wildmatch(theDom, dom2Match)) {
final Map<String,NamedObject> moiTb = domainTb.get(domain);
final Map<String,NamedObject> moiTb = domainTb.get(dom);
if (allNames)
result.addAll(moiTb.values());
else
@ -599,11 +673,21 @@ public class Repository {
* Removes an MBean from the repository.
*
* @param name name of the MBean to remove.
* @param context A registration context. If non null, the repository
* will call {@link RegistrationContext#unregistered()
* context.unregistered()} from within the repository
* lock, just after the mbean associated with
* {@code name} is removed from the repository.
* If {@link RegistrationContext#unregistered()
* context.unregistered()} is not expected to throw any
* exception. If it does, the exception is logged
* and swallowed.
*
* @exception InstanceNotFoundException The MBean does not exist in
* the repository.
*/
public void remove(final ObjectName name)
public void remove(final ObjectName name,
final RegistrationContext context)
throws InstanceNotFoundException {
// Debugging stuff
@ -645,6 +729,9 @@ public class Repository {
if (dom == domain)
domainTb.put(domain, new HashMap<String,NamedObject>());
}
unregistering(context,name);
} finally {
lock.writeLock().unlock();
}

View File

@ -35,6 +35,7 @@ import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanOperationInfo;
import javax.management.ManagedOperation;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationBroadcasterSupport;
@ -118,22 +119,32 @@ class StandardMBeanIntrospector extends MBeanIntrospector<Method> {
@Override
MBeanAttributeInfo getMBeanAttributeInfo(String attributeName,
Method getter, Method setter) {
Method getter, Method setter) throws IntrospectionException {
final String description = "Attribute exposed for management";
try {
return new MBeanAttributeInfo(attributeName, description,
getter, setter);
} catch (IntrospectionException e) {
throw new RuntimeException(e); // should not happen
}
String description = getAttributeDescription(
attributeName, "Attribute exposed for management",
getter, setter);
return new MBeanAttributeInfo(attributeName, description,
getter, setter);
}
@Override
MBeanOperationInfo getMBeanOperationInfo(String operationName,
Method operation) {
final String description = "Operation exposed for management";
return new MBeanOperationInfo(description, operation);
final String defaultDescription = "Operation exposed for management";
String description = Introspector.descriptionForElement(operation);
if (description == null)
description = defaultDescription;
int impact = MBeanOperationInfo.UNKNOWN;
ManagedOperation annot = operation.getAnnotation(ManagedOperation.class);
if (annot != null)
impact = annot.impact().getCode();
MBeanOperationInfo mboi = new MBeanOperationInfo(description, operation);
return new MBeanOperationInfo(
mboi.getName(), mboi.getDescription(), mboi.getSignature(),
mboi.getReturnType(), impact, mboi.getDescriptor());
}
@Override

View File

@ -41,26 +41,24 @@ import javax.management.openmbean.MXBeanMappingFactory;
public class StandardMBeanSupport extends MBeanSupport<Method> {
/**
<p>Construct a Standard MBean that wraps the given resource using the
given Standard MBean interface.</p>
@param resource the underlying resource for the new MBean.
@param mbeanInterface the interface to be used to determine
the MBean's management interface.
@param <T> a type parameter that allows the compiler to check
that {@code resource} implements {@code mbeanInterface},
provided that {@code mbeanInterface} is a class constant like
{@code SomeMBean.class}.
@throws IllegalArgumentException if {@code resource} is null or
if it does not implement the class {@code mbeanInterface} or if
that class is not a valid Standard MBean interface.
*/
public <T> StandardMBeanSupport(T resource, Class<T> mbeanInterface)
* <p>Construct a Standard MBean that wraps the given resource using the
* given Standard MBean interface.</p>
*
* @param resource the underlying resource for the new MBean.
* @param mbeanInterfaceType the class or interface to be used to determine
* the MBean's management interface. An interface if this is a
* classic Standard MBean; a class if this is a {@code @ManagedResource}.
* @param <T> a type parameter that allows the compiler to check
* that {@code resource} implements {@code mbeanInterfaceType},
* provided that {@code mbeanInterfaceType} is a class constant like
* {@code SomeMBean.class}.
* @throws IllegalArgumentException if {@code resource} is null or
* if it does not implement the class {@code mbeanInterfaceType} or if
* that class is not a valid Standard MBean interface.
*/
public <T> StandardMBeanSupport(T resource, Class<T> mbeanInterfaceType)
throws NotCompliantMBeanException {
super(resource, mbeanInterface, (MXBeanMappingFactory) null);
super(resource, mbeanInterfaceType, (MXBeanMappingFactory) null);
}
@Override
@ -86,13 +84,14 @@ public class StandardMBeanSupport extends MBeanSupport<Method> {
@Override
public MBeanInfo getMBeanInfo() {
MBeanInfo mbi = super.getMBeanInfo();
Class<?> resourceClass = getResource().getClass();
if (StandardMBeanIntrospector.isDefinitelyImmutableInfo(resourceClass))
Class<?> resourceClass = getWrappedObject().getClass();
if (!getMBeanInterface().isInterface() ||
StandardMBeanIntrospector.isDefinitelyImmutableInfo(resourceClass))
return mbi;
return new MBeanInfo(mbi.getClassName(), mbi.getDescription(),
mbi.getAttributes(), mbi.getConstructors(),
mbi.getOperations(),
MBeanIntrospector.findNotifications(getResource()),
MBeanIntrospector.findNotifications(getWrappedObject()),
mbi.getDescriptor());
}
}

View File

@ -38,6 +38,7 @@ import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.WeakHashMap;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
@ -71,6 +72,10 @@ public class Util {
return new LinkedHashMap<K, V>();
}
static <K, V> WeakHashMap<K, V> newWeakHashMap() {
return new WeakHashMap<K, V>();
}
static <E> Set<E> newSet() {
return new HashSet<E>();
}

View File

@ -192,6 +192,7 @@ class BinaryRelQueryExp extends QueryEval implements QueryExp {
return "(" + exp1 + ") " + relOpString() + " (" + exp2 + ")";
}
@Override
String toQueryString() {
return exp1 + " " + relOpString() + " " + exp2;
}

View File

@ -0,0 +1,180 @@
/*
* 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;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ResourceBundle;
/**
* <p>The textual description of an MBean or part of an MBean. This
* description is intended to be displayed to users to help them
* understand what the MBean does. Ultimately it will be the value of
* the {@code getDescription()} method of an {@link MBeanInfo}, {@link
* MBeanAttributeInfo}, or similar.</p>
*
* <p>This annotation applies to Standard MBean interfaces and to
* MXBean interfaces, as well as to MBean classes defined using the
* {@link MBean @MBean} or {@link MXBean @MXBean} annotations. For
* example, a Standard MBean might be defined like this:</p>
*
* <pre>
* <b>{@code @Description}</b>("Application configuration")
* public interface ConfigurationMBean {
* <b>{@code @Description}</b>("Cache size in bytes")
* public int getCacheSize();
* public void setCacheSize(int size);
*
* <b>{@code @Description}</b>("Last time the configuration was changed, " +
* "in milliseconds since 1 Jan 1970")
* public long getLastChangedTime();
*
* <b>{@code @Description}</b>("Save the configuration to a file")
* public void save(
* <b>{@code @Description}</b>("Optional name of the file, or null for the default name")
* String fileName);
* }
* </pre>
*
* <p>The {@code MBeanInfo} for this MBean will have a {@link
* MBeanInfo#getDescription() getDescription()} that is {@code
* "Application configuration"}. It will contain an {@code
* MBeanAttributeInfo} for the {@code CacheSize} attribute that is
* defined by the methods {@code getCacheSize} and {@code
* setCacheSize}, and another {@code MBeanAttributeInfo} for {@code
* LastChangedTime}. The {@link MBeanAttributeInfo#getDescription()
* getDescription()} for {@code CacheSize} will be {@code "Cache size
* in bytes"}. Notice that there is no need to add a
* {@code @Description} to both {@code getCacheSize} and {@code
* setCacheSize} - either alone will do. But if you do add a
* {@code @Description} to both, it must be the same.</p>
*
* <p>The {@code MBeanInfo} will also contain an {@link
* MBeanOperationInfo} where {@link
* MBeanOperationInfo#getDescription() getDescription()} is {@code
* "Save the configuration to a file"}. This {@code
* MBeanOperationInfo} will contain an {@link MBeanParameterInfo}
* where {@link MBeanParameterInfo#getDescription() getDescription()}
* is {@code "Optional name of the file, or null for the default
* name"}.</p>
*
* <p>The {@code @Description} annotation can also be applied to the
* public constructors of the implementation class. Continuing the
* above example, the {@code Configuration} class implementing {@code
* ConfigurationMBean} might look like this:</p>
*
* <pre>
* public class Configuration implements ConfigurationMBean {
* <b>{@code @Description}</b>("A Configuration MBean with the default file name")
* public Configuration() {
* this(DEFAULT_FILE_NAME);
* }
*
* <b>{@code @Description}</b>("A Configuration MBean with a specified file name")
* public Configuration(
* <b>{@code @Description}</b>("Name of the file the configuration is stored in")
* String fileName) {...}
* ...
* }
* </pre>
*
* <p>The {@code @Description} annotation also works in MBeans that
* are defined using the {@code @MBean} or {@code @MXBean} annotation
* on classes. Here is an alternative implementation of {@code
* Configuration} that does not use an {@code ConfigurationMBean}
* interface.</p>
*
* <pre>
* <b>{@code @MBean}</b>
* <b>{@code @Description}</b>("Application configuration")
* public class Configuration {
* <b>{@code @Description}</b>("A Configuration MBean with the default file name")
* public Configuration() {
* this(DEFAULT_FILE_NAME);
* }
*
* <b>{@code @Description}</b>("A Configuration MBean with a specified file name")
* public Configuration(
* <b>{@code @Description}</b>("Name of the file the configuration is stored in")
* String fileName) {...}
*
* <b>{@code @ManagedAttribute}</b>
* <b>{@code @Description}</b>("Cache size in bytes")
* public int getCacheSize() {...}
* <b>{@code @ManagedAttribute}</b>
* public void setCacheSize(int size) {...}
*
* <b>{@code @ManagedOperation}</b>
* <b>{@code @Description}</b>("Last time the configuration was changed, " +
* "in milliseconds since 1 Jan 1970")
* public long getLastChangedTime() {...}
*
* <b>{@code @ManagedOperation}</b>
* <b>{@code @Description}</b>("Save the configuration to a file")
* public void save(
* <b>{@code @Description}</b>("Optional name of the file, or null for the default name")
* String fileName) {...}
* ...
* }
* </pre>
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER,
ElementType.TYPE})
public @interface Description {
/**
* <p>The description.</p>
*/
String value();
/**
* <p>The base name for the {@link ResourceBundle} in which the key given in
* the {@code descriptionResourceKey} field can be found, for example
* {@code "com.example.myapp.MBeanResources"}. If a non-default value
* is supplied for this element, it will appear in the
* <a href="Descriptor.html#descriptionResourceBundleBaseName"><!--
* -->{@code Descriptor}</a> for the annotated item.</p>
*/
@DescriptorKey(
value = "descriptionResourceBundleBaseName", omitIfDefault = true)
String bundleBaseName() default "";
/**
* <p>A resource key for the description of this element. In
* conjunction with the {@link #bundleBaseName bundleBaseName},
* this can be used to find a localized version of the description.
* If a non-default value
* is supplied for this element, it will appear in the
* <a href="Descriptor.html#descriptionResourceKey"><!--
* -->{@code Descriptor}</a> for the annotated item.</p>
*/
@DescriptorKey(value = "descriptionResourceKey", omitIfDefault = true)
String key() default "";
}

View File

@ -38,6 +38,7 @@ import java.util.Arrays;
import java.util.ResourceBundle;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.MXBeanMappingFactory;
import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
import javax.management.openmbean.OpenMBeanOperationInfoSupport;
import javax.management.openmbean.OpenMBeanParameterInfoSupport;
@ -117,21 +118,19 @@ import javax.management.openmbean.OpenType;
* deprecation, for example {@code "1.3 Replaced by the Capacity
* attribute"}.</td>
*
* <tr><td>descriptionResource<br>BundleBaseName</td><td>String</td><td>Any</td>
* <tr id="descriptionResourceBundleBaseName">
* <td>descriptionResource<br>BundleBaseName</td><td>String</td><td>Any</td>
*
* <td>The base name for the {@link ResourceBundle} in which the key given in
* the {@code descriptionResourceKey} field can be found, for example
* {@code "com.example.myapp.MBeanResources"}. The meaning of this
* field is defined by this specification but the field is not set or
* used by the JMX API itself.</td>
* {@code "com.example.myapp.MBeanResources"}.</td>
*
* <tr><td>descriptionResourceKey</td><td>String</td><td>Any</td>
* <tr id="descriptionResourceKey">
* <td>descriptionResourceKey</td><td>String</td><td>Any</td>
*
* <td>A resource key for the description of this element. In
* conjunction with the {@code descriptionResourceBundleBaseName},
* this can be used to find a localized version of the description.
* The meaning of this field is defined by this specification but the
* field is not set or used by the JMX API itself.</td>
* this can be used to find a localized version of the description.</td>
*
* <tr><td>enabled</td><td>String</td>
* <td>MBeanAttributeInfo<br>MBeanNotificationInfo<br>MBeanOperationInfo</td>
@ -216,6 +215,14 @@ import javax.management.openmbean.OpenType;
* StandardMBean} class will have this field in its MBeanInfo
* Descriptor.</td>
*
* <tr><td id="mxbeanMappingFactoryClass"><i>mxbeanMappingFactoryClass</i>
* </td><td>String</td>
* <td>MBeanInfo</td>
*
* <td>The name of the {@link MXBeanMappingFactory} class that was used for this
* MXBean, if it was not the {@linkplain MXBeanMappingFactory#DEFAULT default}
* one.</td>
*
* <tr><td><a name="openType"><i>openType</i></a><td>{@link OpenType}</td>
* <td>MBeanAttributeInfo<br>MBeanOperationInfo<br>MBeanParameterInfo</td>
*

View File

@ -0,0 +1,137 @@
/*
* 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;
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;
/**
* <p>Annotation that adds fields to a {@link Descriptor}. This can be the
* Descriptor for an MBean, or for an attribute, operation, or constructor
* in an MBean, or for a parameter of an operation or constructor.</p>
*
* <p>Consider this Standard MBean interface, for example:</p>
*
* <pre>
* public interface CacheControlMBean {
* <b>&#64;DescriptorFields("units=bytes")</b>
* public long getCacheSize();
* }
* </pre>
*
* <p>When a Standard MBean is made using this interface, the usual rules
* mean that it will have an attribute called {@code CacheSize} of type
* {@code long}. The {@code DescriptorFields} annotation will ensure
* that the {@link MBeanAttributeInfo} for this attribute will have a
* {@code Descriptor} that has a field called {@code units} with
* corresponding value {@code bytes}.</p>
*
* <p>Similarly, if the interface looks like this:</p>
*
* <pre>
* public interface CacheControlMBean {
* <b>&#64;DescriptorFields({"units=bytes", "since=1.5"})</b>
* public long getCacheSize();
* }
* </pre>
*
* <p>then the resulting {@code Descriptor} will contain the following
* fields:</p>
*
* <table border="2">
* <tr><th>Name</th><th>Value</th></tr>
* <tr><td>units</td><td>"bytes"</td></tr>
* <tr><td>since</td><td>"1.5"</td></tr>
* </table>
*
* <p>The {@code @DescriptorFields} annotation can be applied to:</p>
*
* <ul>
* <li>a Standard MBean or MXBean interface;
* <li>a method in such an interface;
* <li>a parameter of a method in a Standard MBean or MXBean interface
* when that method is an operation (not a getter or setter for an attribute);
* <li>a public constructor in the class that implements a Standard MBean
* or MXBean;
* <li>a parameter in such a constructor.
* </ul>
*
* <p>Other uses of the annotation will either fail to compile or be
* ignored.</p>
*
* <p>Interface annotations are checked only on the exact interface
* that defines the management interface of a Standard MBean or an
* MXBean, not on its parent interfaces. Method annotations are
* checked only in the most specific interface in which the method
* appears; in other words, if a child interface overrides a method
* from a parent interface, only {@code @DescriptorFields} annotations in
* the method in the child interface are considered.
*
* <p>The Descriptor fields contributed in this way must be consistent
* with each other and with any fields contributed by {@link
* DescriptorKey &#64;DescriptorKey} annotations. That is, two
* different annotations, or two members of the same annotation, must
* not define a different value for the same Descriptor field. Fields
* from annotations on a getter method must also be consistent with
* fields from annotations on the corresponding setter method.</p>
*
* <p>The Descriptor resulting from these annotations will be merged
* with any Descriptor fields provided by the implementation, such as
* the <a href="Descriptor.html#immutableInfo">{@code
* immutableInfo}</a> field for an MBean. The fields from the annotations
* must be consistent with these fields provided by the implementation.</p>
*
* <h4>{@literal @DescriptorFields and @DescriptorKey}</h4>
*
* <p>The {@link DescriptorKey @DescriptorKey} annotation provides
* another way to use annotations to define Descriptor fields.
* <code>&#64;DescriptorKey</code> requires more work but is also more
* robust, because there is less risk of mistakes such as misspelling
* the name of the field or giving an invalid value.
* <code>&#64;DescriptorFields</code> is more convenient but includes
* those risks. <code>&#64;DescriptorFields</code> is more
* appropriate for occasional use, but for a Descriptor field that you
* add in many places, you should consider a purpose-built annotation
* using <code>&#64;DescriptorKey</code>.
*
* @since 1.7
*/
@Documented
@Inherited // for @MBean and @MXBean classes
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD,
ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DescriptorFields {
/**
* <p>The descriptor fields. Each element of the string looks like
* {@code "name=value"}.</p>
*/
public String[] value();
}

View File

@ -33,6 +33,11 @@ import java.lang.annotation.*;
* an MBean, or for an attribute, operation, or constructor in an
* MBean, or for a parameter of an operation or constructor.</p>
*
* <p>(The {@link DescriptorFields @DescriptorFields} annotation
* provides another way to add fields to a {@code Descriptor}. See
* the documentation for that annotation for a comparison of the
* two possibilities.)</p>
*
* <p>Consider this annotation for example:</p>
*
* <pre>
@ -57,7 +62,7 @@ import java.lang.annotation.*;
* <p>When a Standard MBean is made from the {@code CacheControlMBean},
* the usual rules mean that it will have an attribute called
* {@code CacheSize} of type {@code long}. The {@code @Units}
* attribute, given the above definition, will ensure that the
* annotation, given the above definition, will ensure that the
* {@link MBeanAttributeInfo} for this attribute will have a
* {@code Descriptor} that has a field called {@code units} with
* corresponding value {@code bytes}.</p>
@ -125,12 +130,13 @@ import java.lang.annotation.*;
* the method in the child interface are considered.
*
* <p>The Descriptor fields contributed in this way by different
* annotations on the same program element must be consistent. That
* is, two different annotations, or two members of the same
* annotation, must not define a different value for the same
* Descriptor field. Fields from annotations on a getter method must
* also be consistent with fields from annotations on the
* corresponding setter method.</p>
* annotations on the same program element must be consistent with
* each other and with any fields contributed by a {@link
* DescriptorFields &#64;DescriptorFields} annotation. That is, two
* different annotations, or two members of the same annotation, must
* not define a different value for the same Descriptor field. Fields
* from annotations on a getter method must also be consistent with
* fields from annotations on the corresponding setter method.</p>
*
* <p>The Descriptor resulting from these annotations will be merged
* with any Descriptor fields provided by the implementation, such as
@ -169,4 +175,36 @@ import java.lang.annotation.*;
@Target(ElementType.METHOD)
public @interface DescriptorKey {
String value();
/**
* <p>Do not include this field in the Descriptor if the annotation
* element has its default value. For example, suppose {@code @Units} is
* defined like this:</p>
*
* <pre>
* &#64;Documented
* &#64;Target(ElementType.METHOD)
* &#64;Retention(RetentionPolicy.RUNTIME)
* public &#64;interface Units {
* &#64;DescriptorKey("units")
* String value();
*
* <b>&#64;DescriptorKey(value = "descriptionResourceKey",
* omitIfDefault = true)</b>
* String resourceKey() default "";
*
* <b>&#64;DescriptorKey(value = "descriptionResourceBundleBaseName",
* omitIfDefault = true)</b>
* String resourceBundleBaseName() default "";
* }
* </pre>
*
* <p>Then consider a usage such as {@code @Units("bytes")} or
* {@code @Units(value = "bytes", resourceKey = "")}, where the
* {@code resourceKey} and {@code resourceBundleBaseNames} elements
* have their default values. In this case the Descriptor resulting
* from these annotations will not include a {@code descriptionResourceKey}
* or {@code descriptionResourceBundleBaseName} field.</p>
*/
boolean omitIfDefault() default false;
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2005 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;
/**
* <p>An MBean can implement this interface to affect how the MBeanServer's
* {@link MBeanServer#getClassLoaderFor getClassLoaderFor} and
* {@link MBeanServer#isInstanceOf isInstanceOf} methods behave.
* If these methods should refer to a wrapped object rather than the
* MBean object itself, then the {@link #getWrappedObject} method should
* return that wrapped object.</p>
*
* @see MBeanServer#getClassLoaderFor
* @see MBeanServer#isInstanceOf
*/
public interface DynamicWrapperMBean extends DynamicMBean {
/**
* <p>The resource corresponding to this MBean. This is the object whose
* class name should be reflected by the MBean's
* {@link MBeanServer#getMBeanInfo getMBeanInfo()}.<!--
* -->{@link MBeanInfo#getClassName getClassName()} for example. For a "plain"
* DynamicMBean it will be "this". For an MBean that wraps another
* object, in the manner of {@link javax.management.StandardMBean}, it will be the
* wrapped object.</p>
*
* @return The resource corresponding to this MBean.
*/
public Object getWrappedObject();
/**
* <p>The {@code ClassLoader} for this MBean, which can be used to
* retrieve resources associated with the MBean for example. Usually,
* it will be
* {@link #getWrappedObject()}.{@code getClass().getClassLoader()}.
*
* @return The {@code ClassLoader} for this MBean.
*/
public ClassLoader getWrappedClassLoader();
}

View File

@ -0,0 +1,105 @@
/*
* 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;
/**
* <p>Defines the impact of an MBean operation, in particular whether it
* has an effect on the MBean or simply returns information. This enum
* is used in the {@link ManagedOperation @ManagedOperation} annotation.
* Its {@link #getCode()} method can be used to get an {@code int} suitable
* for use as the {@code impact} parameter in an {@link MBeanOperationInfo}
* constructor.</p>
*/
public enum Impact {
/**
* The operation is read-like: it returns information but does not change
* any state.
* @see MBeanOperationInfo#INFO
*/
INFO(MBeanOperationInfo.INFO),
/**
* The operation is write-like: it has an effect but does not return
* any information from the MBean.
* @see MBeanOperationInfo#ACTION
*/
ACTION(MBeanOperationInfo.ACTION),
/**
* The operation is both read-like and write-like: it has an effect,
* and it also returns information from the MBean.
* @see MBeanOperationInfo#ACTION_INFO
*/
ACTION_INFO(MBeanOperationInfo.ACTION_INFO),
/**
* The impact of the operation is unknown or cannot be expressed
* using one of the other values.
* @see MBeanOperationInfo#UNKNOWN
*/
UNKNOWN(MBeanOperationInfo.UNKNOWN);
private final int code;
/**
* An instance of this enumeration, with the corresponding {@code int}
* code used by the {@link MBeanOperationInfo} constructors.
*
* @param code the code used by the {@code MBeanOperationInfo} constructors.
*/
Impact(int code) {
this.code = code;
}
/**
* The equivalent {@code int} code used by the {@link MBeanOperationInfo}
* constructors.
* @return the {@code int} code.
*/
public int getCode() {
return code;
}
/**
* Return the {@code Impact} value corresponding to the given {@code int}
* code. The {@code code} is the value that would be used in an
* {@code MBeanOperationInfo} constructor.
*
* @param code the {@code int} code.
*
* @return an {@code Impact} value {@code x} such that
* {@code code == x.}{@link #getCode()}, or {@code Impact.UNKNOWN}
* if there is no such value.
*/
public static Impact forCode(int code) {
switch (code) {
case MBeanOperationInfo.ACTION: return ACTION;
case MBeanOperationInfo.INFO: return INFO;
case MBeanOperationInfo.ACTION_INFO: return ACTION_INFO;
default: return UNKNOWN;
}
}
}

View File

@ -26,6 +26,7 @@
package javax.management;
import com.sun.jmx.mbeanserver.Introspector;
import com.sun.jmx.mbeanserver.MBeanInjector;
import com.sun.jmx.remote.util.ClassLogger;
import java.beans.BeanInfo;
import java.beans.PropertyDescriptor;
@ -130,6 +131,7 @@ public class JMX {
* </pre>
*
* @see javax.management.JMX.ProxyOptions
* @see javax.management.StandardMBean.Options
*/
public static class MBeanOptions implements Serializable, Cloneable {
private static final long serialVersionUID = -6380842449318177843L;
@ -739,4 +741,28 @@ public class JMX {
// exactly the string "MXBean" since that would mean there
// was no package name, which is pretty unlikely in practice.
}
/**
* <p>Test if an MBean can emit notifications. An MBean can emit
* notifications if either it implements {@link NotificationBroadcaster}
* (perhaps through its child interface {@link NotificationEmitter}), or
* it uses <a href="MBeanRegistration.html#injection">resource
* injection</a> to obtain an instance of {@link SendNotification}
* through which it can send notifications.</p>
*
* @param mbean an MBean object.
* @return true if the given object is a valid MBean that can emit
* notifications; false if the object is a valid MBean but that
* cannot emit notifications.
* @throws NotCompliantMBeanException if the given object is not
* a valid MBean.
*/
public static boolean isNotificationSource(Object mbean)
throws NotCompliantMBeanException {
if (mbean instanceof NotificationBroadcaster)
return true;
Object resource = (mbean instanceof DynamicWrapperMBean) ?
((DynamicWrapperMBean) mbean).getWrappedObject() : mbean;
return (MBeanInjector.injectsSendNotification(resource));
}
}

View File

@ -0,0 +1,68 @@
/*
* 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;
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;
/**
* <p>Indicates that the annotated class is a Standard MBean. A Standard
* MBean class can be defined as in this example:</p>
*
* <pre>
* {@code @MBean}
* public class Configuration {
* {@link ManagedAttribute @ManagedAttribute}
* public int getCacheSize() {...}
* {@code @ManagedAttribute}
* public void setCacheSize(int size);
*
* {@code @ManagedAttribute}
* public long getLastChangedTime();
*
* {@link ManagedOperation @ManagedOperation}
* public void save();
* }
* </pre>
*
* <p>The class must be public. Public methods within the class can be
* annotated with {@code @ManagedOperation} to indicate that they are
* MBean operations. Public getter and setter methods within the class
* can be annotated with {@code @ManagedAttribute} to indicate that they define
* MBean attributes.</p>
*
* <p>If the MBean is to be an MXBean rather than a Standard MBean, then
* the {@link MXBean @MXBean} annotation must be used instead of
* {@code @MBean}.</p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface MBean {
}

View File

@ -46,25 +46,30 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable {
new MBeanOperationInfo[0];
/**
* Indicates that the operation is read-like,
* it basically returns information.
* Indicates that the operation is read-like:
* it returns information but does not change any state.
* @see Impact#INFO
*/
public static final int INFO = 0;
/**
* Indicates that the operation is a write-like,
* and would modify the MBean in some way, typically by writing some value
* or changing a configuration.
* Indicates that the operation is write-like: it has an effect but does
* not return any information from the MBean.
* @see Impact#ACTION
*/
public static final int ACTION = 1;
/**
* Indicates that the operation is both read-like and write-like.
* Indicates that the operation is both read-like and write-like:
* it has an effect, and it also returns information from the MBean.
* @see Impact#ACTION_INFO
*/
public static final int ACTION_INFO = 2;
/**
* Indicates that the operation has an "unknown" nature.
* Indicates that the impact of the operation is unknown or cannot be
* expressed using one of the other values.
* @see Impact#UNKNOWN
*/
public static final int UNKNOWN = 3;
@ -120,8 +125,9 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable {
* describing the parameters(arguments) of the method. This may be
* null with the same effect as a zero-length array.
* @param type The type of the method's return value.
* @param impact The impact of the method, one of <CODE>INFO,
* ACTION, ACTION_INFO, UNKNOWN</CODE>.
* @param impact The impact of the method, one of
* {@link #INFO}, {@link #ACTION}, {@link #ACTION_INFO},
* {@link #UNKNOWN}.
*/
public MBeanOperationInfo(String name,
String description,
@ -140,8 +146,9 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable {
* describing the parameters(arguments) of the method. This may be
* null with the same effect as a zero-length array.
* @param type The type of the method's return value.
* @param impact The impact of the method, one of <CODE>INFO,
* ACTION, ACTION_INFO, UNKNOWN</CODE>.
* @param impact The impact of the method, one of
* {@link #INFO}, {@link #ACTION}, {@link #ACTION_INFO},
* {@link #UNKNOWN}.
* @param descriptor The descriptor for the operation. This may be null
* which is equivalent to an empty descriptor.
*
@ -319,9 +326,14 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable {
for (int i = 0; i < classes.length; i++) {
Descriptor d = Introspector.descriptorForAnnotations(annots[i]);
final String pn = "p" + (i + 1);
params[i] =
new MBeanParameterInfo(pn, classes[i].getName(), "", d);
String description = Introspector.descriptionForParameter(annots[i]);
if (description == null)
description = "";
String name = Introspector.nameForParameter(annots[i]);
if (name == null)
name = "p" + (i + 1);
params[i] = new MBeanParameterInfo(
name, classes[i].getName(), description, d);
}
return params;

View File

@ -27,9 +27,101 @@ package javax.management;
/**
* Can be implemented by an MBean in order to
* <p>Can be implemented by an MBean in order to
* carry out operations before and after being registered or unregistered from
* the MBean server.
* the MBean Server. An MBean can also implement this interface in order
* to get a reference to the MBean Server and/or its name within that
* MBean Server.</p>
*
* <h4 id="injection">Resource injection</h4>
*
* <p>As an alternative to implementing {@code MBeanRegistration}, if all that
* is needed is the MBean Server or ObjectName then an MBean can use
* <em>resource injection</em>.</p>
*
* <p>If a field in the MBean object has type {@link ObjectName} and has
* the {@link javax.annotation.Resource &#64;Resource} annotation,
* then the {@code ObjectName} under which the MBean is registered is
* assigned to that field during registration. Likewise, if a field has type
* {@link MBeanServer} and the <code>&#64;Resource</code> annotation, then it will
* be set to the {@code MBeanServer} in which the MBean is registered.</p>
*
* <p>For example:</p>
*
* <pre>
* public Configuration implements ConfigurationMBean {
* &#64;Resource
* private volatile MBeanServer mbeanServer;
* &#64;Resource
* private volatile ObjectName objectName;
* ...
* void unregisterSelf() throws Exception {
* mbeanServer.unregisterMBean(objectName);
* }
* }
* </pre>
*
* <p>Resource injection can also be used on fields of type
* {@link SendNotification} to simplify notification sending. Such a field
* will get a reference to an object of type {@code SendNotification} when
* the MBean is registered, and it can use this reference to send notifications.
* For example:</p>
*
* <pre>
* public Configuration implements ConfigurationMBean {
* &#64;Resource
* private volatile SendNotification sender;
* ...
* private void updated() {
* Notification n = new Notification(...);
* sender.sendNotification(n);
* }
* }
* </pre>
*
* <p>A field to be injected must not be static. It is recommended that
* such fields be declared {@code volatile}.</p>
*
* <p>It is also possible to use the <code>&#64;Resource</code> annotation on
* methods. Such a method must have a {@code void} return type and a single
* argument of the appropriate type, for example {@code ObjectName}.</p>
*
* <p>Any number of fields and methods may have the <code>&#64;Resource</code>
* annotation. All fields and methods with type {@code ObjectName}
* (for example) will receive the same {@code ObjectName} value.</p>
*
* <p>Resource injection is available for all types of MBeans, not just
* Standard MBeans.</p>
*
* <p>If an MBean implements the {@link DynamicWrapperMBean} interface then
* resource injection happens on the object returned by that interface's
* {@link DynamicWrapperMBean#getWrappedObject() getWrappedObject()} method
* rather than on the MBean object itself.
*
* <p>Resource injection happens after the {@link #preRegister preRegister}
* method is called (if any), and before the MBean is actually registered
* in the MBean Server. If a <code>&#64;Resource</code> method throws
* an exception, the effect is the same as if {@code preRegister} had
* thrown the exception. In particular it will prevent the MBean from being
* registered.</p>
*
* <p>Resource injection can be used on a field or method where the type
* is a parent of the injected type, if the injected type is explicitly
* specified in the <code>&#64;Resource</code> annotation. For example:</p>
*
* <pre>
* &#64;Resource(type = MBeanServer.class)
* private volatile MBeanServerConnection mbsc;
* </pre>
*
* <p>Formally, suppose <em>R</em> is the type in the <code>&#64;Resource</code>
* annotation and <em>T</em> is the type of the method parameter or field.
* Then one of <em>R</em> and <em>T</em> must be a subtype of the other
* (or they must be the same type). Injection happens if this subtype
* is {@code MBeanServer}, {@code ObjectName}, or {@code SendNotification}.
* Otherwise the <code>&#64;Resource</code> annotation is ignored.</p>
*
* <p>Resource injection in MBeans is new in version 2.0 of the JMX API.</p>
*
* @since 1.5
*/
@ -38,12 +130,12 @@ public interface MBeanRegistration {
/**
* Allows the MBean to perform any operations it needs before
* being registered in the MBean server. If the name of the MBean
* being registered in the MBean Server. If the name of the MBean
* is not specified, the MBean can provide a name for its
* registration. If any exception is raised, the MBean will not be
* registered in the MBean server.
* registered in the MBean Server.
*
* @param server The MBean server in which the MBean will be registered.
* @param server The MBean Server in which the MBean will be registered.
*
* @param name The object name of the MBean. This name is null if
* the name parameter to one of the <code>createMBean</code> or
@ -57,7 +149,7 @@ public interface MBeanRegistration {
* the returned value.
*
* @exception java.lang.Exception This exception will be caught by
* the MBean server and re-thrown as an {@link
* the MBean Server and re-thrown as an {@link
* MBeanRegistrationException}.
*/
public ObjectName preRegister(MBeanServer server,

View File

@ -61,7 +61,7 @@ import javax.management.loading.ClassLoaderRepository;
* <CODE>ObjectName</CODE> is: <BR>
* <CODE>JMImplementation:type=MBeanServerDelegate</CODE>.</p>
*
* <p>An object obtained from the {@link
* <p id="security">An object obtained from the {@link
* MBeanServerFactory#createMBeanServer(String) createMBeanServer} or
* {@link MBeanServerFactory#newMBeanServer(String) newMBeanServer}
* methods of the {@link MBeanServerFactory} class applies security
@ -661,13 +661,16 @@ public interface MBeanServer extends MBeanServerConnection {
ReflectionException;
/**
* <p>Return the {@link java.lang.ClassLoader} that was used for
* loading the class of the named MBean.</p>
* <p>Return the {@link java.lang.ClassLoader} that was used for loading
* the class of the named MBean. If the MBean implements the {@link
* DynamicWrapperMBean} interface, then the returned value is the
* result of the {@link DynamicWrapperMBean#getWrappedClassLoader()}
* method.</p>
*
* @param mbeanName The ObjectName of the MBean.
*
* @return The ClassLoader used for that MBean. If <var>l</var>
* is the MBean's actual ClassLoader, and <var>r</var> is the
* is the value specified by the rules above, and <var>r</var> is the
* returned value, then either:
*
* <ul>

View File

@ -839,6 +839,12 @@ public interface MBeanServerConnection {
*
* <p>Otherwise, the result is false.</p>
*
* <p>If the MBean implements the {@link DynamicWrapperMBean}
* interface, then in the above rules X is the result of the MBean's {@link
* DynamicWrapperMBean#getWrappedObject() getWrappedObject()} method and L
* is the result of its {@link DynamicWrapperMBean#getWrappedClassLoader()
* getWrappedClassLoader()} method.
*
* @param name The <CODE>ObjectName</CODE> of the MBean.
* @param className The name of the class.
*

View File

@ -27,6 +27,7 @@ package javax.management;
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;
@ -57,11 +58,13 @@ import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
/**
<p>Annotation to mark an interface explicitly as being an MXBean
interface, or as not being an MXBean interface. By default, an
<p>Annotation to mark a class or interface explicitly as being an MXBean,
or as not being an MXBean. By default, an
interface is an MXBean interface if its name ends with {@code
MXBean}, as in {@code SomethingMXBean}. The following interfaces
are MXBean interfaces:</p>
MXBean}, as in {@code SomethingMXBean}. A class is never an MXBean by
default.</p>
<p>The following interfaces are MXBean interfaces:</p>
<pre>
public interface WhatsitMXBean {}
@ -82,6 +85,11 @@ import javax.management.openmbean.TabularType;
public interface MisleadingMXBean {}
</pre>
<p>A class can be annotated with {@code @MXBean} to indicate that it
is an MXBean. In this case, its methods should have <code>&#64;{@link
ManagedAttribute}</code> or <code>&#64;{@link ManagedOperation}</code>
annotations, as described for <code>&#64;{@link MBean}</code>.</p>
<h3 id="MXBean-spec">MXBean specification</h3>
<p>The MXBean concept provides a simple way to code an MBean
@ -1246,9 +1254,24 @@ public interface Node {
@since 1.6
*/
/*
* This annotation is @Inherited because if an MXBean is defined as a
* class using annotations, then its subclasses are also MXBeans.
* For example:
* @MXBean
* public class Super {
* @ManagedAttribute
* public String getName() {...}
* }
* public class Sub extends Super {}
* Here Sub is an MXBean.
*
* The @Inherited annotation has no effect when applied to an interface.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface MXBean {
/**
True if the annotated interface is an MXBean interface.

View File

@ -0,0 +1,64 @@
/*
* 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;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <p>Indicates that a method in an MBean class defines an MBean attribute.
* This annotation must be applied to a public method of a public class
* that is itself annotated with an {@link MBean @MBean} or
* {@link MXBean @MXBean} annotation, or inherits such an annotation from
* a superclass.</p>
*
* <p>The annotated method must be a getter or setter. In other words,
* it must look like one of the following...</p>
*
* <pre>
* <i>T</i> get<i>Foo</i>()
* void set<i>Foo</i>(<i>T</i> param)
* </pre>
*
* <p>...where <i>{@code T}</i> is any type and <i>{@code Foo}</i> is the
* name of the attribute. For any attribute <i>{@code Foo}</i>, if only
* a {@code get}<i>{@code Foo}</i> method has a {@code ManagedAttribute}
* annotation, then <i>{@code Foo}</i> is a read-only attribute. If only
* a {@code set}<i>{@code Foo}</i> method has a {@code ManagedAttribute}
* annotation, then <i>{@code Foo}</i> is a write-only attribute. If
* both {@code get}<i>{@code Foo}</i> and {@code set}<i>{@code Foo}</i>
* methods have the annotation, then <i>{@code Foo}</i> is a read-write
* attribute. In this last case, the type <i>{@code T}</i> must be the
* same in both methods.</p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ManagedAttribute {
}

View File

@ -0,0 +1,67 @@
/*
* 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;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <p>Indicates that a method in an MBean class defines an MBean operation.
* This annotation can be applied to:</p>
*
* <ul>
* <li>A public method of a public class
* that is itself annotated with an {@link MBean @MBean} or
* {@link MXBean @MXBean} annotation, or inherits such an annotation from
* a superclass.</li>
* <li>A method of an MBean or MXBean interface.
* </ul>
*
* <p>Every method in an MBean or MXBean interface defines an MBean
* operation even without this annotation, but the annotation allows
* you to specify the impact of the operation:</p>
*
* <pre>
* public interface ConfigurationMBean {
* {@code @ManagedOperation}(impact = {@link Impact#ACTION Impact.ACTION})
* public void save();
* ...
* }
* </pre>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ManagedOperation {
/**
* <p>The impact of this operation, as shown by
* {@link MBeanOperationInfo#getImpact()}.
*/
Impact impact() default Impact.UNKNOWN;
}

View File

@ -91,6 +91,7 @@ class NotQueryExp extends QueryEval implements QueryExp {
return "not (" + exp + ")";
}
@Override
String toQueryString() {
return "not (" + Query.toString(exp) + ")";
}

View File

@ -58,7 +58,8 @@ import com.sun.jmx.remote.util.ClassLogger;
*
* @since 1.5
*/
public class NotificationBroadcasterSupport implements NotificationEmitter {
public class NotificationBroadcasterSupport
implements NotificationEmitter, SendNotification {
/**
* Constructs a NotificationBroadcasterSupport where each listener is invoked by the
* thread sending the notification. This constructor is equivalent to

View File

@ -0,0 +1,117 @@
/*
* 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;
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;
/**
* <p>Specifies the kinds of notification an MBean can emit. In both the
* following examples, the MBean emits notifications of type
* {@code "com.example.notifs.create"} and of type
* {@code "com.example.notifs.destroy"}:</p>
*
* <pre>
* // Example one: a Standard MBean
* {@code @NotificationInfo}(types={"com.example.notifs.create",
* "com.example.notifs.destroy"})
* public interface CacheMBean {...}
*
* public class Cache implements CacheMBean {...}
* </pre>
*
* <pre>
* // Example two: an annotated MBean
* {@link MBean @MBean}
* {@code @NotificationInfo}(types={"com.example.notifs.create",
* "com.example.notifs.destroy"})
* public class Cache {...}
* </pre>
*
* <p>Each {@code @NotificationInfo} produces an {@link
* MBeanNotificationInfo} inside the {@link MBeanInfo} of each MBean
* to which the annotation applies.</p>
*
* <p>If you need to specify different notification classes, or different
* descriptions for different notification types, then you can group
* several {@code @NotificationInfo} annotations into a containing
* {@link NotificationInfos @NotificationInfos} annotation.
*
* <p>The {@code NotificationInfo} and {@code NotificationInfos}
* annotations can be applied to the MBean implementation class, or to
* any parent class or interface. These annotations on a class take
* precedence over annotations on any superclass or superinterface.
* If an MBean does not have these annotations on its class or any
* superclass, then superinterfaces are examined. It is an error for
* more than one superinterface to have these annotations, unless one
* of them is a child of all the others.</p>
*/
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotificationInfo {
/**
* <p>The {@linkplain Notification#getType() notification types}
* that this MBean can emit.</p>
*/
String[] types();
/**
* <p>The class that emitted notifications will have. It is recommended
* that this be {@link Notification}, or one of its standard subclasses
* in the JMX API.</p>
*/
Class<? extends Notification> notificationClass() default Notification.class;
/**
* <p>The description of this notification. For example:
*
* <pre>
* {@code @NotificationInfo}(
* types={"com.example.notifs.create"},
* description={@code @Description}("object created"))
* </pre>
*/
Description description() default @Description("");
/**
* <p>Additional descriptor fields for the derived {@code
* MBeanNotificationInfo}. They are specified in the same way as
* for the {@link DescriptorFields @DescriptorFields} annotation,
* for example:</p>
* <pre>
* {@code @NotificationInfo}(
* types={"com.example.notifs.create"},
* descriptorFields={"severity=6"})
* </pre>
*/
String[] descriptorFields() default {};
}

View File

@ -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;
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.remote.JMXConnectionNotification;
/**
* <p>Specifies the kinds of notification an MBean can emit, when this
* cannot be represented by a single {@link NotificationInfo
* &#64;NotificationInfo} annotation.</p>
*
* <p>For example, this annotation specifies that an MBean can emit
* {@link AttributeChangeNotification} and {@link
* JMXConnectionNotification}:</p>
*
* <pre>
* {@code @NotificationInfos}(
* {@code @NotificationInfo}(
* types = {{@link AttributeChangeNotification#ATTRIBUTE_CHANGE}},
* notificationClass = AttributeChangeNotification.class),
* {@code @NotificationInfo}(
* types = {{@link JMXConnectionNotification#OPENED},
* {@link JMXConnectionNotification#CLOSED}},
* notificationClass = JMXConnectionNotification.class)
* )
* </pre>
*
* <p>If an MBean has both {@code NotificationInfo} and {@code
* NotificationInfos} on the same class or interface, the effect is
* the same as if the {@code NotificationInfo} were moved inside the
* {@code NotificationInfos}.</p>
*/
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotificationInfos {
/**
* <p>The {@link NotificationInfo} annotations.</p>
*/
NotificationInfo[] value();
}

View File

@ -0,0 +1,38 @@
/*
* 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;
/**
* Interface implemented by objects that can be asked to send a notification.
*/
public interface SendNotification {
/**
* Sends a notification.
*
* @param notification The notification to send.
*/
public void sendNotification(Notification notification);
}

View File

@ -25,6 +25,9 @@
package javax.management;
import com.sun.jmx.mbeanserver.MBeanInjector;
import static javax.management.JMX.MBeanOptions;
/**
* <p>An MBean whose management interface is determined by reflection
* on a Java interface, and that emits notifications.</p>
@ -62,7 +65,7 @@ package javax.management;
* @since 1.6
*/
public class StandardEmitterMBean extends StandardMBean
implements NotificationEmitter {
implements NotificationEmitter, SendNotification {
private final NotificationEmitter emitter;
private final MBeanNotificationInfo[] notificationInfo;
@ -76,9 +79,10 @@ public class StandardEmitterMBean extends StandardMBean
* for {@code implementation} and {@code emitter} to be the same object.</p>
*
* <p>If {@code emitter} is an instance of {@code
* NotificationBroadcasterSupport} then the MBean's {@link #sendNotification
* SendNotification} (for example, a {@link NotificationBroadcasterSupport}),
* then the MBean's {@link #sendNotification
* sendNotification} method will call {@code emitter.}{@link
* NotificationBroadcasterSupport#sendNotification sendNotification}.</p>
* SendNotification#sendNotification sendNotification}.</p>
*
* <p>The array returned by {@link #getNotificationInfo()} on the
* new MBean is a copy of the array returned by
@ -90,20 +94,18 @@ public class StandardEmitterMBean extends StandardMBean
*
* @param implementation the implementation of the MBean interface.
* @param mbeanInterface a Standard MBean interface.
* @param emitter the object that will handle notifications.
* @param emitter the object that will handle notifications. If null,
* a new {@code NotificationEmitter} will be constructed that also
* implements {@link SendNotification}.
*
* @throws IllegalArgumentException if the {@code mbeanInterface}
* does not follow JMX design patterns for Management Interfaces, or
* if the given {@code implementation} does not implement the
* specified interface, or if {@code emitter} is null.
* specified interface.
*/
public <T> StandardEmitterMBean(T implementation, Class<T> mbeanInterface,
NotificationEmitter emitter) {
super(implementation, mbeanInterface, false);
if (emitter == null)
throw new IllegalArgumentException("Null emitter");
this.emitter = emitter;
this.notificationInfo = emitter.getNotificationInfo();
this(implementation, mbeanInterface, false, emitter);
}
/**
@ -118,9 +120,10 @@ public class StandardEmitterMBean extends StandardMBean
* same object.</p>
*
* <p>If {@code emitter} is an instance of {@code
* NotificationBroadcasterSupport} then the MBean's {@link #sendNotification
* SendNotification} (for example, a {@link NotificationBroadcasterSupport}),
* then the MBean's {@link #sendNotification
* sendNotification} method will call {@code emitter.}{@link
* NotificationBroadcasterSupport#sendNotification sendNotification}.</p>
* SendNotification#sendNotification sendNotification}.</p>
*
* <p>The array returned by {@link #getNotificationInfo()} on the
* new MBean is a copy of the array returned by
@ -134,21 +137,69 @@ public class StandardEmitterMBean extends StandardMBean
* @param mbeanInterface a Standard MBean interface.
* @param isMXBean If true, the {@code mbeanInterface} parameter
* names an MXBean interface and the resultant MBean is an MXBean.
* @param emitter the object that will handle notifications.
* @param emitter the object that will handle notifications. If null,
* a new {@code NotificationEmitter} will be constructed that also
* implements {@link SendNotification}.
*
* @throws IllegalArgumentException if the {@code mbeanInterface}
* does not follow JMX design patterns for Management Interfaces, or
* if the given {@code implementation} does not implement the
* specified interface, or if {@code emitter} is null.
* specified interface.
*/
public <T> StandardEmitterMBean(T implementation, Class<T> mbeanInterface,
boolean isMXBean,
NotificationEmitter emitter) {
super(implementation, mbeanInterface, isMXBean);
this(implementation, mbeanInterface,
isMXBean ? MBeanOptions.MXBEAN : null, emitter);
}
/**
* <p>Make an MBean whose management interface is specified by {@code
* mbeanInterface}, with the given implementation and options, and where
* notifications are handled by the given {@code NotificationEmitter}.
* Options select whether to make a Standard MBean or an MXBean, and
* whether the result of {@link #getWrappedObject()} is the {@code
* StandardEmitterMBean} object or the given implementation. The resultant
* MBean implements the {@code NotificationEmitter} interface by forwarding
* its methods to {@code emitter}. It is legal and useful for {@code
* implementation} and {@code emitter} to be the same object.</p>
*
* <p>If {@code emitter} is an instance of {@code
* SendNotification} (for example, a {@link NotificationBroadcasterSupport}),
* then the MBean's {@link #sendNotification
* sendNotification} method will call {@code emitter.}{@link
* SendNotification#sendNotification sendNotification}.</p>
*
* <p>The array returned by {@link #getNotificationInfo()} on the
* new MBean is a copy of the array returned by
* {@code emitter.}{@link NotificationBroadcaster#getNotificationInfo
* getNotificationInfo()} at the time of construction. If the array
* returned by {@code emitter.getNotificationInfo()} later changes,
* that will have no effect on this object's
* {@code getNotificationInfo()}.</p>
*
* @param implementation the implementation of the MBean interface.
* @param mbeanInterface a Standard MBean interface.
* @param options MBeanOptions that control the operation of the resulting
* MBean.
* @param emitter the object that will handle notifications. If null,
* a new {@code NotificationEmitter} will be constructed that also
* implements {@link SendNotification}.
*
* @throws IllegalArgumentException if the {@code mbeanInterface}
* does not follow JMX design patterns for Management Interfaces, or
* if the given {@code implementation} does not implement the
* specified interface.
*/
public <T> StandardEmitterMBean(T implementation, Class<T> mbeanInterface,
MBeanOptions options,
NotificationEmitter emitter) {
super(implementation, mbeanInterface, options);
if (emitter == null)
throw new IllegalArgumentException("Null emitter");
emitter = defaultEmitter();
this.emitter = emitter;
this.notificationInfo = emitter.getNotificationInfo();
injectEmitter();
}
/**
@ -159,9 +210,10 @@ public class StandardEmitterMBean extends StandardMBean
* by forwarding its methods to {@code emitter}.</p>
*
* <p>If {@code emitter} is an instance of {@code
* NotificationBroadcasterSupport} then the MBean's {@link #sendNotification
* SendNotification} (for example, a {@link NotificationBroadcasterSupport}),
* then the MBean's {@link #sendNotification
* sendNotification} method will call {@code emitter.}{@link
* NotificationBroadcasterSupport#sendNotification sendNotification}.</p>
* SendNotification#sendNotification sendNotification}.</p>
*
* <p>The array returned by {@link #getNotificationInfo()} on the
* new MBean is a copy of the array returned by
@ -175,20 +227,17 @@ public class StandardEmitterMBean extends StandardMBean
* the given {@code mbeanInterface}.</p>
*
* @param mbeanInterface a StandardMBean interface.
* @param emitter the object that will handle notifications.
* @param emitter the object that will handle notifications. If null,
* a new {@code NotificationEmitter} will be constructed that also
* implements {@link SendNotification}.
*
* @throws IllegalArgumentException if the {@code mbeanInterface}
* does not follow JMX design patterns for Management Interfaces, or
* if {@code this} does not implement the specified interface, or
* if {@code emitter} is null.
* if {@code this} does not implement the specified interface.
*/
protected StandardEmitterMBean(Class<?> mbeanInterface,
NotificationEmitter emitter) {
super(mbeanInterface, false);
if (emitter == null)
throw new IllegalArgumentException("Null emitter");
this.emitter = emitter;
this.notificationInfo = emitter.getNotificationInfo();
this(mbeanInterface, false, emitter);
}
/**
@ -200,9 +249,10 @@ public class StandardEmitterMBean extends StandardMBean
* forwarding its methods to {@code emitter}.</p>
*
* <p>If {@code emitter} is an instance of {@code
* NotificationBroadcasterSupport} then the MBean's {@link #sendNotification
* SendNotification} (for example, a {@link NotificationBroadcasterSupport}),
* then the MBean's {@link #sendNotification
* sendNotification} method will call {@code emitter.}{@link
* NotificationBroadcasterSupport#sendNotification sendNotification}.</p>
* SendNotification#sendNotification sendNotification}.</p>
*
* <p>The array returned by {@link #getNotificationInfo()} on the
* new MBean is a copy of the array returned by
@ -218,20 +268,86 @@ public class StandardEmitterMBean extends StandardMBean
* @param mbeanInterface a StandardMBean interface.
* @param isMXBean If true, the {@code mbeanInterface} parameter
* names an MXBean interface and the resultant MBean is an MXBean.
* @param emitter the object that will handle notifications.
* @param emitter the object that will handle notifications. If null,
* a new {@code NotificationEmitter} will be constructed that also
* implements {@link SendNotification}.
*
* @throws IllegalArgumentException if the {@code mbeanInterface}
* does not follow JMX design patterns for Management Interfaces, or
* if {@code this} does not implement the specified interface, or
* if {@code emitter} is null.
* if {@code this} does not implement the specified interface.
*/
protected StandardEmitterMBean(Class<?> mbeanInterface, boolean isMXBean,
NotificationEmitter emitter) {
super(mbeanInterface, isMXBean);
this(mbeanInterface, isMXBean ? MBeanOptions.MXBEAN : null, emitter);
}
/**
* <p>Make an MBean whose management interface is specified by {@code
* mbeanInterface}, with the given options, and where notifications are
* handled by the given {@code NotificationEmitter}. This constructor can
* be used to make either Standard MBeans or MXBeans. The resultant MBean
* implements the {@code NotificationEmitter} interface by forwarding its
* methods to {@code emitter}.</p>
*
* <p>If {@code emitter} is an instance of {@code
* SendNotification} (for example, a {@link NotificationBroadcasterSupport}),
* then the MBean's {@link #sendNotification
* sendNotification} method will call {@code emitter.}{@link
* SendNotification#sendNotification sendNotification}.</p>
*
* <p>The array returned by {@link #getNotificationInfo()} on the
* new MBean is a copy of the array returned by
* {@code emitter.}{@link NotificationBroadcaster#getNotificationInfo
* getNotificationInfo()} at the time of construction. If the array
* returned by {@code emitter.getNotificationInfo()} later changes,
* that will have no effect on this object's
* {@code getNotificationInfo()}.</p>
*
* <p>This constructor must be called from a subclass that implements
* the given {@code mbeanInterface}.</p>
*
* @param mbeanInterface a StandardMBean interface.
* @param options MBeanOptions that control the operation of the resulting
* MBean.
* @param emitter the object that will handle notifications. If null,
* a new {@code NotificationEmitter} will be constructed that also
* implements {@link SendNotification}.
*
* @throws IllegalArgumentException if the {@code mbeanInterface}
* does not follow JMX design patterns for Management Interfaces, or
* if {@code this} does not implement the specified interface.
*/
protected StandardEmitterMBean(Class<?> mbeanInterface, MBeanOptions options,
NotificationEmitter emitter) {
super(mbeanInterface, options);
if (emitter == null)
throw new IllegalArgumentException("Null emitter");
emitter = defaultEmitter();
this.emitter = emitter;
this.notificationInfo = emitter.getNotificationInfo();
injectEmitter();
}
private NotificationEmitter defaultEmitter() {
MBeanNotificationInfo[] mbnis = getNotificationInfo();
// Will be null unless getNotificationInfo() is overridden,
// since the notificationInfo field has not been set at this point.
if (mbnis == null)
mbnis = getMBeanInfo().getNotifications();
return new NotificationBroadcasterSupport(mbnis);
}
private void injectEmitter() {
if (emitter instanceof SendNotification) {
try {
Object resource = getImplementation();
SendNotification send = (SendNotification) emitter;
MBeanInjector.injectSendNotification(resource, send);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
}
public void removeNotificationListener(NotificationListener listener)
@ -259,10 +375,10 @@ public class StandardEmitterMBean extends StandardMBean
/**
* <p>Sends a notification.</p>
*
* <p>If the {@code emitter} parameter to the constructor was an
* instance of {@code NotificationBroadcasterSupport} then this
* method will call {@code emitter.}{@link
* NotificationBroadcasterSupport#sendNotification
* <p>If the {@code emitter} parameter to the constructor was
* an instance of {@link SendNotification}, such as {@link
* NotificationBroadcasterSupport}, then this method will call {@code
* emitter.}{@link SendNotification#sendNotification
* sendNotification}.</p>
*
* @param n the notification to send.
@ -271,13 +387,12 @@ public class StandardEmitterMBean extends StandardMBean
* constructor was not a {@code NotificationBroadcasterSupport}.
*/
public void sendNotification(Notification n) {
if (emitter instanceof NotificationBroadcasterSupport)
((NotificationBroadcasterSupport) emitter).sendNotification(n);
if (emitter instanceof SendNotification)
((SendNotification) emitter).sendNotification(n);
else {
final String msg =
"Cannot sendNotification when emitter is not an " +
"instance of NotificationBroadcasterSupport: " +
emitter.getClass().getName();
"instance of SendNotification: " + emitter.getClass().getName();
throw new ClassCastException(msg);
}
}
@ -292,6 +407,7 @@ public class StandardEmitterMBean extends StandardMBean
* @param info The default MBeanInfo derived by reflection.
* @return the MBeanNotificationInfo[] for the new MBeanInfo.
*/
@Override
MBeanNotificationInfo[] getNotifications(MBeanInfo info) {
return getNotificationInfo();
}

View File

@ -27,6 +27,7 @@ package javax.management;
import com.sun.jmx.mbeanserver.DescriptorCache;
import com.sun.jmx.mbeanserver.Introspector;
import com.sun.jmx.mbeanserver.MBeanInjector;
import com.sun.jmx.mbeanserver.MBeanSupport;
import com.sun.jmx.mbeanserver.MXBeanSupport;
import com.sun.jmx.mbeanserver.StandardMBeanSupport;
@ -125,7 +126,78 @@ import static javax.management.JMX.MBeanOptions;
*
* @since 1.5
*/
public class StandardMBean implements DynamicMBean, MBeanRegistration {
public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration {
/**
* <p>Options controlling the behavior of {@code StandardMBean} instances.</p>
*/
public static class Options extends JMX.MBeanOptions {
private static final long serialVersionUID = 5107355471177517164L;
private boolean wrappedVisible;
/**
* <p>Construct an {@code Options} object where all options have
* their default values.</p>
*/
public Options() {}
@Override
public Options clone() {
return (Options) super.clone();
}
/**
* <p>Defines whether the {@link StandardMBean#getWrappedObject()
* getWrappedObject} method returns the wrapped object.</p>
*
* <p>If this option is true, then {@code getWrappedObject()} will return
* the same object as {@link StandardMBean#getImplementation()
* getImplementation}. Otherwise, it will return the
* StandardMBean instance itself. The setting of this option
* affects the behavior of {@link MBeanServer#getClassLoaderFor
* MBeanServer.getClassLoaderFor} and {@link MBeanServer#isInstanceOf
* MBeanServer.isInstanceOf}. The default value is false for
* compatibility reasons, but true is a better value for most new code.</p>
*
* @return true if this StandardMBean's {@link
* StandardMBean#getWrappedObject getWrappedObject} returns the wrapped
* object.
*/
public boolean isWrappedObjectVisible() {
return this.wrappedVisible;
}
/**
* <p>Set the {@link #isWrappedObjectVisible WrappedObjectVisible} option
* to the given value.</p>
* @param visible the new value.
*/
public void setWrappedObjectVisible(boolean visible) {
this.wrappedVisible = visible;
}
// Canonical objects for each of (MXBean,!MXBean) x (WVisible,!WVisible)
private static final Options[] CANONICALS = {
new Options(), new Options(), new Options(), new Options(),
};
static {
CANONICALS[1].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT);
CANONICALS[2].setWrappedObjectVisible(true);
CANONICALS[3].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT);
CANONICALS[3].setWrappedObjectVisible(true);
}
@Override
MBeanOptions[] canonicals() {
return CANONICALS;
}
@Override
boolean same(MBeanOptions opts) {
return (super.same(opts) && opts instanceof Options &&
((Options) opts).wrappedVisible == wrappedVisible);
}
}
private final static DescriptorCache descriptors =
DescriptorCache.getInstance(JMX.proof);
@ -347,7 +419,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
* 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.
* MBean.
* @param <T> Allows the compiler to check
* that {@code implementation} does indeed implement the class
* described by {@code mbeanInterface}. The compiler can only
@ -381,7 +453,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
* @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.
* MBean.
*
* @exception IllegalArgumentException if the <var>mbeanInterface</var>
* does not follow JMX design patterns for Management Interfaces, or
@ -441,7 +513,67 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
* @see #setImplementation
**/
public Object getImplementation() {
return mbean.getResource();
return mbean.getWrappedObject();
}
/**
* <p>Get the wrapped implementation object or return this object.</p>
*
* <p>For compatibility reasons, this method only returns the wrapped
* implementation object if the {@link Options#isWrappedObjectVisible
* WrappedObjectVisible} option was specified when this StandardMBean
* was created. Otherwise it returns {@code this}.</p>
*
* <p>If you want the MBeanServer's {@link MBeanServer#getClassLoaderFor
* getClassLoaderFor} and {@link MBeanServer#isInstanceOf
* isInstanceOf} methods to refer to the wrapped implementation and
* not this StandardMBean object, then you must set the
* {@code WrappedObjectVisible} option, for example using:</p>
*
* <pre>
* StandardMBean.Options opts = new StandardMBean.Options();
* opts.setWrappedObjectVisible(true);
* StandardMBean mbean = new StandardMBean(impl, MyMBean.class, opts);
* </pre>
*
* @return The wrapped implementation object, or this StandardMBean
* instance.
*/
public Object getWrappedObject() {
if (options instanceof Options &&
((Options) options).isWrappedObjectVisible())
return getImplementation();
else
return this;
}
/**
* <p>Get the ClassLoader of the wrapped implementation object or of this
* object.</p>
*
* <p>For compatibility reasons, this method only returns the ClassLoader
* of the wrapped implementation object if the {@link
* Options#isWrappedObjectVisible WrappedObjectVisible} option was
* specified when this StandardMBean was created. Otherwise it returns
* {@code this.getClass().getClassLoader()}.</p>
*
* <p>If you want the MBeanServer's {@link MBeanServer#getClassLoaderFor
* getClassLoaderFor} and {@link MBeanServer#isInstanceOf
* isInstanceOf} methods to refer to the wrapped implementation and
* not this StandardMBean object, then you must set the
* {@code WrappedObjectVisible} option, for example using:</p>
*
* <pre>
* StandardMBean.Options opts = new StandardMBean.Options();
* opts.setWrappedObjectVisible(true);
* StandardMBean mbean = new StandardMBean(impl, MyMBean.class, opts);
* </pre>
*
* @return The ClassLoader of the wrapped Cimplementation object, or of
* this StandardMBean instance.
*/
public ClassLoader getWrappedClassLoader() {
return getWrappedObject().getClass().getClassLoader();
}
/**
@ -457,7 +589,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
* @return The class of the implementation of this Standard MBean (or MXBean).
**/
public Class<?> getImplementationClass() {
return mbean.getResource().getClass();
return mbean.getWrappedObject().getClass();
}
/**
@ -559,7 +691,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
MBeanSupport msupport = mbean;
final MBeanInfo bi = msupport.getMBeanInfo();
final Object impl = msupport.getResource();
final Object impl = msupport.getWrappedObject();
final boolean immutableInfo = immutableInfo(this.getClass());
@ -1184,6 +1316,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception {
mbean.register(server, name);
MBeanInjector.inject(mbean.getWrappedObject(), server, name);
return name;
}

View File

@ -23,7 +23,7 @@
* have any questions.
*/
/*
* @author IBM Corp.
* @(#)author IBM Corp.
*
* Copyright IBM Corp. 1999-2000. All rights reserved.
*/
@ -55,6 +55,7 @@ import javax.management.AttributeChangeNotificationFilter;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.Descriptor;
import javax.management.DynamicWrapperMBean;
import javax.management.InstanceNotFoundException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
@ -115,7 +116,7 @@ import sun.reflect.misc.ReflectUtil;
*/
public class RequiredModelMBean
implements ModelMBean, MBeanRegistration, NotificationEmitter {
implements ModelMBean, MBeanRegistration, NotificationEmitter, DynamicWrapperMBean {
/*************************************/
/* attributes */
@ -133,6 +134,9 @@ public class RequiredModelMBean
* and operations will be executed */
private Object managedResource = null;
/* true if getWrappedObject returns the wrapped resource */
private boolean visible;
/* records the registering in MBeanServer */
private boolean registered = false;
private transient MBeanServer server = null;
@ -318,9 +322,13 @@ public class RequiredModelMBean
*
* @param mr Object that is the managed resource
* @param mr_type The type of reference for the managed resource.
* <br>Can be: "ObjectReference", "Handle", "IOR", "EJBHandle",
* or "RMIReference".
* <br>In this implementation only "ObjectReference" is supported.
* <br>Can be: "ObjectReference", "VisibleObjectReference",
* "Handle", "IOR", "EJBHandle", or "RMIReference".
* <br>In this implementation only "ObjectReference" and
* "VisibleObjectReference" are supported. The two
* types are equivalent except for the behavior of the
* {@link #getWrappedObject()} and {@link #getWrappedClassLoader()}
* methods.
*
* @exception MBeanException The initializer of the object has
* thrown an exception.
@ -340,10 +348,11 @@ public class RequiredModelMBean
"setManagedResource(Object,String)","Entry");
}
visible = "visibleObjectReference".equalsIgnoreCase(mr_type);
// check that the mr_type is supported by this JMXAgent
// only "objectReference" is supported
if ((mr_type == null) ||
(! mr_type.equalsIgnoreCase("objectReference"))) {
if (!"objectReference".equalsIgnoreCase(mr_type) && !visible) {
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
@ -368,6 +377,51 @@ public class RequiredModelMBean
}
}
/**
* <p>Get the managed resource for this Model MBean. For compatibility
* reasons, the managed resource is only returned if the resource type
* specified to {@link #setManagedResource setManagedResource} was {@code
* "visibleObjectReference"}. Otherwise, {@code this} is returned.</p>
*
* @return The value that was specified to {@link #setManagedResource
* setManagedResource}, if the resource type is {@code
* "visibleObjectReference"}. Otherwise, {@code this}.
*/
public Object getWrappedObject() {
if (visible)
return managedResource;
else
return this;
}
/**
* <p>Get the ClassLoader of the managed resource for this Model MBean. For
* compatibility reasons, the ClassLoader of the managed resource is only
* returned if the resource type specified to {@link #setManagedResource
* setManagedResource} was {@code "visibleObjectReference"}. Otherwise,
* {@code this.getClass().getClassLoader()} is returned.</p>
*
* @return The ClassLoader of the value that was specified to
* {@link #setManagedResource setManagedResource}, if the resource
* type is {@code "visibleObjectReference"}. Otherwise, {@code
* this.getClass().getClassLoader()}.
*/
public ClassLoader getWrappedClassLoader() {
return getWrappedObject().getClass().getClassLoader();
}
private static boolean isTrue(Descriptor d, String field) {
if (d == null)
return false;
Object x = d.getFieldValue(field);
if (x instanceof Boolean)
return (Boolean) x;
if (!(x instanceof String))
return false;
String s = (String) x;
return ("true".equalsIgnoreCase(s) || "T".equalsIgnoreCase(s));
}
/**
* <p>Instantiates this MBean instance with the data found for
* the MBean in the persistent store. The data loaded could include

View File

@ -38,14 +38,17 @@ have any questions.
so within the access control context of the
{@link javax.management.monitor.Monitor#start} caller.</p>
<p>The value being monitored can be a simple value contained within a
complex type. For example, the {@link java.lang.management.MemoryMXBean
MemoryMXBean} defined in <tt>java.lang.management</tt> has an attribute
<tt>HeapMemoryUsage</tt> of type {@link java.lang.management.MemoryUsage
MemoryUsage}. To monitor the amount of <i>used</i> memory, described by
the <tt>used</tt> property of <tt>MemoryUsage</tt>, you could monitor
"<tt>HeapMemoryUsage.used</tt>". That string would be the argument to
{@link javax.management.monitor.MonitorMBean#setObservedAttribute(String)
<p id="complex">The value being monitored can be a simple value
contained within a complex type. For example, the {@link
java.lang.management.MemoryMXBean MemoryMXBean} defined in
<tt>java.lang.management</tt> has an attribute
<tt>HeapMemoryUsage</tt> of type {@link
java.lang.management.MemoryUsage MemoryUsage}. To monitor the
amount of <i>used</i> memory, described by the <tt>used</tt>
property of <tt>MemoryUsage</tt>, you could monitor
"<tt>HeapMemoryUsage.used</tt>". That string would be the
argument to {@link
javax.management.monitor.MonitorMBean#setObservedAttribute(String)
setObservedAttribute}.</p>
<p>The rules used to interpret an <tt>ObservedAttribute</tt> like

View File

@ -56,41 +56,41 @@ have any questions.
resource. It has a <em>management interface</em> consisting
of:</p>
<ul>
<li>named and typed attributes that can be read and/or
written</li>
<li>named and typed operations that can be invoked</li>
<ul>
<li>named and typed attributes that can be read and/or
written</li>
<li>typed notifications that can be emitted by the MBean.</li>
</ul>
<li>named and typed operations that can be invoked</li>
<p>For example, an MBean representing an application's
configuration could have attributes representing the different
configuration items. Reading the <code>CacheSize</code>
attribute would return the current value of that item.
Writing it would update the item, potentially changing the
behavior of the running application. An operation such as
<code>save</code> could store the current configuration
persistently. A notification such as
<code>ConfigurationChangedNotification</code> could be sent
every time the configuration is changed.</p>
<li>typed notifications that can be emitted by the MBean.</li>
</ul>
<p>In the standard usage of the JMX API, MBeans are implemented
as Java objects. However, as explained below, these objects are
not usually referenced directly.</p>
<p>For example, an MBean representing an application's
configuration could have attributes representing the different
configuration items. Reading the <code>CacheSize</code>
attribute would return the current value of that item.
Writing it would update the item, potentially changing the
behavior of the running application. An operation such as
<code>save</code> could store the current configuration
persistently. A notification such as
<code>ConfigurationChangedNotification</code> could be sent
every time the configuration is changed.</p>
<p>In the standard usage of the JMX API, MBeans are implemented
as Java objects. However, as explained below, these objects are
not usually referenced directly.</p>
<h3>Standard MBeans</h3>
<h3>Standard MBeans</h3>
<p>To make MBean implementation simple, the JMX API includes the
notion of <em>Standard MBeans</em>. A Standard MBean is one
whose attributes and operations are deduced from a Java
interface using certain naming patterns, similar to those used
by JavaBeans<sup><font size="-1">TM</font></sup>. For
example, consider an interface like this:</p>
<p>To make MBean implementation simple, the JMX API includes the
notion of <em>Standard MBeans</em>. A Standard MBean is one
whose attributes and operations are deduced from a Java
interface using certain naming patterns, similar to those used
by JavaBeans<sup><font size="-1">TM</font></sup>. For
example, consider an interface like this:</p>
<pre>
<pre>
public interface ConfigurationMBean {
public int getCacheSize();
public void setCacheSize(int size);
@ -128,107 +128,148 @@ have any questions.
class.</p>
<h3>MXBeans</h3>
<p>An <em>MXBean</em> is a variant of Standard MBean where complex
types are mapped to a standard set of types defined in the
{@link javax.management.openmbean} package. MXBeans are appropriate
if you would otherwise need to reference application-specific
classes in your MBean interface. They are described in detail
in the specification for {@link javax.management.MXBean MXBean}.
<h3 id="stdannot">Defining Standard MBeans with annotations</h3>
<p>As an alternative to creating an interface such as
<code>ConfigurationMBean</code> and a class that implements it,
you can write just the class, and use annotations to pick out the
public methods that will make up the management interface. For
example, the following class has the same management interface
as a <code>Configuration</code> class that implements the
<code>ConfigurationMBean</code> interface above.</p>
<pre>
{@link javax.management.MBean @MBean}
public class Configuration {
{@link javax.management.ManagedAttribute @ManagedAttribute}
public int getCacheSize() {...}
&#64;ManagedAttribute
public void setCacheSize(int size) {...}
&#64;ManagedAttribute
public long getLastChangedTime() {...}
{@link javax.management.ManagedOperation @ManagedOperation}
public void save() {...}
...
}
</pre>
<p>This approach simplifies development, but it does have two
potential drawbacks. First, if you run the Javadoc tool on
this class, the documentation of the management interface may
be mixed in with the documentation of non-management methods
in the class. Second, you cannot make a proxy
as described <a href="#proxy">below</a> if you do not have an
interface like <code>ConfigurationMBean</code>.</p>
<h3>Dynamic MBeans</h3>
<h3>MXBeans</h3>
<p>A <em>Dynamic MBean</em> is an MBean that defines its
management interface at run-time. For example, a configuration
MBean could determine the names and types of the attributes it
exposes by parsing an XML file.</p>
<p>An <em>MXBean</em> is a variant of Standard MBean where complex
types are mapped to a standard set of types defined in the
{@link javax.management.openmbean} package. MXBeans are appropriate
if you would otherwise need to reference application-specific
classes in your MBean interface. They are described in detail
in the specification for {@link javax.management.MXBean MXBean}.</p>
<p>Any Java object of a class that implements the {@link
javax.management.DynamicMBean DynamicMBean} interface is a
Dynamic MBean.</p>
<p>You can define MXBeans using annotations as described
in the <a href="#stdannot">previous section</a>, but
using the <code>&#64;MXBean</code> annotation instead of
<code>&#64;MBean</code>.</p>
<h3>Open MBeans</h3>
<h3>Dynamic MBeans</h3>
<p>An <em>Open MBean</em> is a kind of Dynamic MBean where the
types of attributes and of operation parameters and return
values are built using a small set of predefined Java classes.
Open MBeans facilitate operation with remote management programs
that do not necessarily have access to application-specific
types, including non-Java programs. Open MBeans are defined by
the package <a href="openmbean/package-summary.html"><code>
javax.management.openmbean</code></a>.</p>
<p>A <em>Dynamic MBean</em> is an MBean that defines its
management interface at run-time. For example, a configuration
MBean could determine the names and types of the attributes it
exposes by parsing an XML file.</p>
<p>Any Java object of a class that implements the {@link
javax.management.DynamicMBean DynamicMBean} interface is a
Dynamic MBean.</p>
<h3>Model MBeans</h3>
<h3>Open MBeans</h3>
<p>A <em>Model MBean</em> is a kind of Dynamic MBean that acts
as a bridge between the management interface and the
underlying managed resource. Both the management interface and
the managed resource are specified as Java objects. The same
Model MBean implementation can be reused many times with
different management interfaces and managed resources, and it can
provide common functionality such as persistence and caching.
Model MBeans are defined by the package
<a href="modelmbean/package-summary.html"><code>
javax.management.modelmbean</code></a>.</p>
<p>An <em>Open MBean</em> is a kind of Dynamic MBean where the
types of attributes and of operation parameters and return
values are built using a small set of predefined Java classes.
Open MBeans facilitate operation with remote management programs
that do not necessarily have access to application-specific
types, including non-Java programs. Open MBeans are defined by
the package <a href="openmbean/package-summary.html"><code>
javax.management.openmbean</code></a>.</p>
<h2>MBean Server</h2>
<p>To be useful, an MBean must be registered in an <em>MBean
Server</em>. An MBean Server is a repository of MBeans.
Usually the only access to the MBeans is through the MBean
Server. In other words, code no longer accesses the Java
object implementing the MBean directly, but instead accesses
the MBean by name through the MBean Server. Each MBean has a
unique name within the MBean Server, defined by the {@link
javax.management.ObjectName ObjectName} class.</p>
<p>An MBean Server is an object implementing the interface
{@link javax.management.MBeanServer MBeanServer}.
The most convenient MBean Server to use is the
<em>Platform MBean Server</em>. This is a
single MBean Server that can be shared by different managed
components running within the same Java Virtual Machine. The
Platform MBean Server is accessed with the method {@link
java.lang.management.ManagementFactory#getPlatformMBeanServer()}.</p>
<h3>Model MBeans</h3>
<p>Application code can also create a new MBean Server, or
access already-created MBean Servers, using the {@link
javax.management.MBeanServerFactory MBeanServerFactory} class.</p>
<p>A <em>Model MBean</em> is a kind of Dynamic MBean that acts
as a bridge between the management interface and the
underlying managed resource. Both the management interface and
the managed resource are specified as Java objects. The same
Model MBean implementation can be reused many times with
different management interfaces and managed resources, and it can
provide common functionality such as persistence and caching.
Model MBeans are defined by the package
<a href="modelmbean/package-summary.html"><code>
javax.management.modelmbean</code></a>.</p>
<h3>Creating MBeans in the MBean Server</h3>
<h2>MBean Server</h2>
<p>There are two ways to create an MBean. One is to construct a
Java object that will be the MBean, then use the {@link
javax.management.MBeanServer#registerMBean registerMBean}
method to register it in the MBean Server. The other is to
create and register the MBean in a single operation using one
of the {@link javax.management.MBeanServer#createMBean(String,
javax.management.ObjectName) createMBean} methods.</p>
<p>To be useful, an MBean must be registered in an <em>MBean
Server</em>. An MBean Server is a repository of MBeans.
Usually the only access to the MBeans is through the MBean
Server. In other words, code no longer accesses the Java
object implementing the MBean directly, but instead accesses
the MBean by name through the MBean Server. Each MBean has a
unique name within the MBean Server, defined by the {@link
javax.management.ObjectName ObjectName} class.</p>
<p>The <code>registerMBean</code> method is simpler for local
use, but cannot be used remotely. The
<code>createMBean</code> method can be used remotely, but
sometimes requires attention to class loading issues.</p>
<p>An MBean Server is an object implementing the interface
{@link javax.management.MBeanServer MBeanServer}.
The most convenient MBean Server to use is the
<em>Platform MBean Server</em>. This is a
single MBean Server that can be shared by different managed
components running within the same Java Virtual Machine. The
Platform MBean Server is accessed with the method {@link
java.lang.management.ManagementFactory#getPlatformMBeanServer()}.</p>
<p>An MBean can perform actions when it is registered in or
unregistered from an MBean Server if it implements the {@link
javax.management.MBeanRegistration MBeanRegistration}
interface.</p>
<p>Application code can also create a new MBean Server, or
access already-created MBean Servers, using the {@link
javax.management.MBeanServerFactory MBeanServerFactory} class.</p>
<h3>Accessing MBeans in the MBean Server</h3>
<h3>Creating MBeans in the MBean Server</h3>
<p>Given an <code>ObjectName</code> <code>name</code> and an
<code>MBeanServer</code> <code>mbs</code>, you can access
attributes and operations as in this example:</p>
<p>There are two ways to create an MBean. One is to construct a
Java object that will be the MBean, then use the {@link
javax.management.MBeanServer#registerMBean registerMBean}
method to register it in the MBean Server. The other is to
create and register the MBean in a single operation using one
of the {@link javax.management.MBeanServer#createMBean(String,
javax.management.ObjectName) createMBean} methods.</p>
<pre>
<p>The <code>registerMBean</code> method is simpler for local
use, but cannot be used remotely. The
<code>createMBean</code> method can be used remotely, but
sometimes requires attention to class loading issues.</p>
<p>An MBean can perform actions when it is registered in or
unregistered from an MBean Server if it implements the {@link
javax.management.MBeanRegistration MBeanRegistration}
interface.</p>
<h3>Accessing MBeans in the MBean Server</h3>
<p>Given an <code>ObjectName</code> <code>name</code> and an
<code>MBeanServer</code> <code>mbs</code>, you can access
attributes and operations as in this example:</p>
<pre>
int cacheSize = mbs.getAttribute(name, "CacheSize");
{@link javax.management.Attribute Attribute} newCacheSize =
new Attribute("CacheSize", new Integer(2000));
@ -236,9 +277,9 @@ have any questions.
mbs.invoke(name, "save", new Object[0], new Class[0]);
</pre>
<p>Alternatively, if you have a Java interface that corresponds
to the management interface for the MBean, you can use an
<em>MBean proxy</em> like this:</p>
<p id="proxy">Alternatively, if you have a Java interface that
corresponds to the management interface for the MBean, you can use an
<em>MBean proxy</em> like this:</p>
<pre>
ConfigurationMBean conf =
@ -264,66 +305,116 @@ have any questions.
perform the query.</p>
<h2>Notifications</h2>
<h3>MBean lifecycle and resource injection</h3>
<p>A <em>notification</em> is an instance of the {@link
javax.management.Notification Notification} class or a
subclass. In addition to its Java class, it has a
<em>type</em> string that can distinguish it from other
notifications of the same class.</p>
<p>An MBean can implement the {@link javax.management.MBeanRegistration
MBeanRegistration} interface in order to be told when it is registered
and unregistered in the MBean Server. Additionally, the {@link
javax.management.MBeanRegistration#preRegister preRegister} method
allows the MBean to get a reference to the <code>MBeanServer</code>
object and to get its <code>ObjectName</code> within the MBean
Server.</p>
<p>An MBean that will emit notifications must implement the
{@link javax.management.NotificationBroadcaster
NotificationBroadcaster} or {@link
javax.management.NotificationEmitter NotificationEmitter}
interface. Usually, it does this by subclassing {@link
javax.management.NotificationBroadcasterSupport
NotificationBroadcasterSupport} or by delegating to an instance
of that class.</p>
<p>Notifications can be received by a <em>listener</em>, which
is an object that implements the {@link
javax.management.NotificationListener NotificationListener}
interface. You can add a listener to an MBean with the method
{@link
javax.management.MBeanServer#addNotificationListener(ObjectName,
NotificationListener, NotificationFilter, Object)}.
You can optionally supply a <em>filter</em> to this method, to
select only notifications of interest. A filter is an object
that implements the {@link javax.management.NotificationFilter
NotificationFilter} interface.</p>
<p>An MBean can be a listener for notifications emitted by other
MBeans in the same MBean Server. In this case, it implements
{@link javax.management.NotificationListener
NotificationListener} and the method {@link
javax.management.MBeanServer#addNotificationListener(ObjectName,
ObjectName, NotificationFilter, Object)} is used to listen.</p>
<p>If the only reason to implement <code>MBeanRegistration</code> is to
discover the <code>MBeanServer</code> and <code>ObjectName</code>, <a
href="MBeanRegistration.html#injection">resource injection</a> may be
more convenient.</p>
<h2>Remote Access to MBeans</h2>
<h2>Notifications</h2>
<p>An MBean Server can be accessed remotely through a
<em>connector</em>. A connector allows a remote Java
application to access an MBean Server in essentially the same
way as a local one. The package
<a href="remote/package-summary.html"><code>
javax.management.remote</code></a> defines connectors.</p>
<p>A <em>notification</em> is an instance of the {@link
javax.management.Notification Notification} class or a
subclass. In addition to its Java class, it has a
<em>type</em> string that can distinguish it from other
notifications of the same class.</p>
<p>The JMX specification also defines the notion of an
<em>adaptor</em>. An adaptor translates between requests in a
protocol such as SNMP or HTML and accesses to an MBean Server.
So for example an SNMP GET operation might result in a
<code>getAttribute</code> on the MBean Server.</p>
<p>If an MBean is to emit notifications, it must do one of two things.</p>
<p id="spec">
@see <a href="{@docRoot}/../technotes/guides/jmx/index.html">
Java SE 6 Platform documentation on JMX technology</a>
in particular the
<a href="{@docRoot}/../technotes/guides/jmx/JMX_1_4_specification.pdf">
JMX Specification, version 1.4(pdf).</a>
<ul>
<li>It can implement the interface {@link
javax.management.NotificationEmitter NotificationEmitter} (or
its parent {@link javax.management.NotificationBroadcaster
NotificationBroadcaster}), usually by subclassing
{@link javax.management.NotificationBroadcasterSupport
NotificationBroadcasterSupport} or delegating to an instance of
that class.</li>
<li>It can use <a href="MBeanRegistration.html#injection">resource
injection</a> to obtain a {@link javax.management.SendNotification
SendNotification} object that it can use to send
notifications.</li>
</ul>
@since 1.5
<p>The two classes below illustrate these two techniques:</p>
<pre>
// Implementing NotificationEmitter (via NotificationBroadcasterSupport)
public class Configuration <b>extends NotificationBroadcasterSupport</b>
implements ConfigurationMBean {
...
private void updated() {
Notification n = new Notification(...);
<b>{@link javax.management.NotificationBroadcasterSupport#sendNotification
sendNotification}(n)</b>;
}
}
// Getting a SendNotification through resource injection
public class Configuration implements ConfigurationMBean {
<b>&#64;Resource</b>
private volatile SendNotification sender;
...
private void updated() {
Notification n = new Notification(...);
<b>sender.sendNotification(n)</b>;
}
}
</pre>
<p>Notifications can be received by a <em>listener</em>, which
is an object that implements the {@link
javax.management.NotificationListener NotificationListener}
interface. You can add a listener to an MBean with the method
{@link
javax.management.MBeanServer#addNotificationListener(ObjectName,
NotificationListener, NotificationFilter, Object)}.
You can optionally supply a <em>filter</em> to this method, to
select only notifications of interest. A filter is an object
that implements the {@link javax.management.NotificationFilter
NotificationFilter} interface.</p>
<p>An MBean can be a listener for notifications emitted by other
MBeans in the same MBean Server. In this case, it implements
{@link javax.management.NotificationListener
NotificationListener} and the method {@link
javax.management.MBeanServer#addNotificationListener(ObjectName,
ObjectName, NotificationFilter, Object)} is used to listen.</p>
<h2>Remote Access to MBeans</h2>
<p>An MBean Server can be accessed remotely through a
<em>connector</em>. A connector allows a remote Java
application to access an MBean Server in essentially the same
way as a local one. The package
<a href="remote/package-summary.html"><code>
javax.management.remote</code></a> defines connectors.</p>
<p>The JMX specification also defines the notion of an
<em>adaptor</em>. An adaptor translates between requests in a
protocol such as SNMP or HTML and accesses to an MBean Server.
So for example an SNMP GET operation might result in a
<code>getAttribute</code> on the MBean Server.</p>
<p id="spec">
@see <a href="{@docRoot}/../technotes/guides/jmx/index.html">
Java SE 6 Platform documentation on JMX technology</a>
in particular the
<a href="{@docRoot}/../technotes/guides/jmx/JMX_1_4_specification.pdf">
JMX Specification, version 1.4(pdf).</a>
@since 1.5
</body>
</html>

View File

@ -0,0 +1,337 @@
/*
* 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 6323980
* @summary Test MBeans defined with &#64;MBean
* @author Eamonn McManus
* @run main/othervm -ea AnnotatedMBeanTest
*/
import java.io.File;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.management.Attribute;
import javax.management.Descriptor;
import javax.management.DescriptorKey;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MXBean;
import javax.management.MalformedObjectNameException;
import javax.management.ManagedAttribute;
import javax.management.ManagedOperation;
import javax.management.MBean;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeType;
public class AnnotatedMBeanTest {
private static MBeanServer mbs;
private static final ObjectName objectName;
static {
try {
objectName = new ObjectName("test:type=Test");
} catch (MalformedObjectNameException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Exception {
if (!AnnotatedMBeanTest.class.desiredAssertionStatus())
throw new Exception("Test must be run with -ea");
File policyFile = File.createTempFile("jmxperms", ".policy");
policyFile.deleteOnExit();
PrintWriter pw = new PrintWriter(policyFile);
pw.println("grant {");
pw.println(" permission javax.management.MBeanPermission \"*\", \"*\";");
pw.println(" permission javax.management.MBeanServerPermission \"*\";");
pw.println(" permission javax.management.MBeanTrustPermission \"*\";");
pw.println("};");
pw.close();
System.setProperty("java.security.policy", policyFile.getAbsolutePath());
System.setSecurityManager(new SecurityManager());
String failure = null;
for (Method m : AnnotatedMBeanTest.class.getDeclaredMethods()) {
if (Modifier.isStatic(m.getModifiers()) &&
m.getName().startsWith("test") &&
m.getParameterTypes().length == 0) {
mbs = MBeanServerFactory.newMBeanServer();
try {
m.invoke(null);
System.out.println(m.getName() + " OK");
} catch (InvocationTargetException ite) {
System.out.println(m.getName() + " got exception:");
Throwable t = ite.getCause();
t.printStackTrace(System.out);
failure = m.getName() + ": " + t.toString();
}
}
}
if (failure == null)
System.out.println("TEST PASSED");
else
throw new Exception("TEST FAILED: " + failure);
}
public static class Stats {
private final int used;
private final int size;
private final boolean interesting;
public Stats(int used, int size, boolean interesting) {
this.used = used;
this.size = size;
this.interesting = interesting;
}
public int getUsed() {
return used;
}
public int getSize() {
return size;
}
public boolean isInteresting() {
return interesting;
}
}
@Retention(RetentionPolicy.RUNTIME)
public static @interface Units {
@DescriptorKey("units")
String value();
}
@MBean
public static class Cache {
private int used = 23;
private int size = 99;
@ManagedAttribute
@Units("bytes")
public int getUsed() {
return used;
}
@ManagedAttribute
public int getSize() {
return size;
}
@ManagedAttribute
public void setSize(int x) {
this.size = x;
}
@ManagedAttribute
public boolean isInteresting() {
return false;
}
@ManagedAttribute
public Stats getStats() {
return new Stats(used, size, false);
}
@ManagedOperation
public int dropOldest(int n) {
return 55;
}
private void irrelevantMethod() {}
private int getIrrelevant() {return 0;}
public int getIrrelevant2() {return 0;}
public int otherIrrelevantMethod() {return 5;}
}
public static class SubCache extends Cache {
// SubCache does not have the @MBean annotation
// but its parent does. It doesn't add any @ManagedAttribute or
// @ManagedOperation methods, so its management interface
// should be the same.
private void irrelevantMethod2() {}
public int otherIrrelevantMethod3() {return 0;}
public int getX() {return 0;}
public void setX(int x) {}
}
@MXBean
public static class CacheMX {
private int used = 23;
private int size = 99;
@ManagedAttribute
@Units("bytes")
public int getUsed() {
return used;
}
@ManagedAttribute
public int getSize() {
return size;
}
@ManagedAttribute
public void setSize(int x) {
this.size = x;
}
@ManagedAttribute
public boolean isInteresting() {
return false;
}
@ManagedAttribute
public Stats getStats() {
return new Stats(used, size, false);
}
@ManagedOperation
public int dropOldest(int n) {
return 55;
}
private void irrelevantMethod() {}
private int getIrrelevant() {return 0;}
public int getIrrelevant2() {return 0;}
public int otherIrrelevantMethod() {return 5;}
}
public static class SubCacheMX extends CacheMX {
private void irrelevantMethod2() {}
public int otherIrrelevantMethod3() {return 0;}
public int getX() {return 0;}
public void setX(int x) {}
}
private static void testSimpleManagedResource() throws Exception {
testResource(new Cache(), false);
}
private static void testSubclassManagedResource() throws Exception {
testResource(new SubCache(), false);
}
private static void testMXBeanResource() throws Exception {
testResource(new CacheMX(), true);
}
private static void testSubclassMXBeanResource() throws Exception {
testResource(new SubCacheMX(), true);
}
private static void testResource(Object resource, boolean mx) throws Exception {
mbs.registerMBean(resource, objectName);
MBeanInfo mbi = mbs.getMBeanInfo(objectName);
assert mbi.getDescriptor().getFieldValue("mxbean").equals(Boolean.toString(mx));
MBeanAttributeInfo[] mbais = mbi.getAttributes();
assert mbais.length == 4: mbais.length;
for (MBeanAttributeInfo mbai : mbais) {
String name = mbai.getName();
if (name.equals("Used")) {
assert mbai.isReadable();
assert !mbai.isWritable();
assert !mbai.isIs();
assert mbai.getType().equals("int");
assert "bytes".equals(mbai.getDescriptor().getFieldValue("units"));
} else if (name.equals("Size")) {
assert mbai.isReadable();
assert mbai.isWritable();
assert !mbai.isIs();
assert mbai.getType().equals("int");
} else if (name.equals("Interesting")) {
assert mbai.isReadable();
assert !mbai.isWritable();
assert mbai.isIs();
assert mbai.getType().equals("boolean");
} else if (name.equals("Stats")) {
assert mbai.isReadable();
assert !mbai.isWritable();
assert !mbai.isIs();
Descriptor d = mbai.getDescriptor();
if (mx) {
assert mbai.getType().equals(CompositeData.class.getName());
assert d.getFieldValue("originalType").equals(Stats.class.getName());
CompositeType ct = (CompositeType) d.getFieldValue("openType");
Set<String> names = new HashSet<String>(
Arrays.asList("used", "size", "interesting"));
assert ct.keySet().equals(names) : ct.keySet();
} else {
assert mbai.getType().equals(Stats.class.getName());
}
} else
assert false : name;
}
MBeanOperationInfo[] mbois = mbi.getOperations();
assert mbois.length == 1: mbois.length;
MBeanOperationInfo mboi = mbois[0];
assert mboi.getName().equals("dropOldest");
assert mboi.getReturnType().equals("int");
MBeanParameterInfo[] mbpis = mboi.getSignature();
assert mbpis.length == 1: mbpis.length;
assert mbpis[0].getType().equals("int");
assert mbs.getAttribute(objectName, "Used").equals(23);
assert mbs.getAttribute(objectName, "Size").equals(99);
mbs.setAttribute(objectName, new Attribute("Size", 55));
assert mbs.getAttribute(objectName, "Size").equals(55);
assert mbs.getAttribute(objectName, "Interesting").equals(false);
Object stats = mbs.getAttribute(objectName, "Stats");
assert (mx ? CompositeData.class : Stats.class).isInstance(stats) : stats.getClass();
int ret = (Integer) mbs.invoke(
objectName, "dropOldest", new Object[] {66}, new String[] {"int"});
assert ret == 55;
}
}

View File

@ -0,0 +1,271 @@
/*
* 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 6323980
* @summary Test &#64;NotificationInfo annotation
* @author Eamonn McManus
* @run main/othervm -ea AnnotatedNotificationInfoTest
*/
import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import javax.annotation.Resource;
import javax.management.AttributeChangeNotification;
import javax.management.Description;
import javax.management.Descriptor;
import javax.management.ImmutableDescriptor;
import javax.management.MBean;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanServer;
import javax.management.MXBean;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationInfo;
import javax.management.NotificationInfos;
import javax.management.ObjectName;
import javax.management.SendNotification;
public class AnnotatedNotificationInfoTest {
// Data for the first test. This tests that MBeanNotificationInfo
// is correctly derived from @NotificationInfo.
// Every static field called mbean* is expected to be an MBean
// with a single MBeanNotificationInfo that has the same value
// in each case.
@NotificationInfo(
types = {"foo", "bar"},
notificationClass = AttributeChangeNotification.class,
description = @Description(
value = "description",
bundleBaseName = "bundle",
key = "key"),
descriptorFields = {"foo=bar"})
public static interface Intf1MBean {}
public static class Intf1
extends NotificationBroadcasterSupport implements Intf1MBean {}
private static Object mbeanIntf1 = new Intf1();
@NotificationInfos(
@NotificationInfo(
types = {"foo", "bar"},
notificationClass = AttributeChangeNotification.class,
description = @Description(
value = "description",
bundleBaseName = "bundle",
key = "key"),
descriptorFields = {"foo=bar"}))
public static interface Intf2MBean {}
public static class Intf2
extends NotificationBroadcasterSupport implements Intf2MBean {}
private static Object mbeanIntf2 = new Intf2();
@NotificationInfos({})
@NotificationInfo(
types = {"foo", "bar"},
notificationClass = AttributeChangeNotification.class,
description = @Description(
value = "description",
bundleBaseName = "bundle",
key = "key"),
descriptorFields = {"foo=bar"})
public static interface Intf3MBean {}
public static class Intf3
extends NotificationBroadcasterSupport implements Intf3MBean {}
private static Object mbeanIntf3 = new Intf3();
@NotificationInfo(
types = {"foo", "bar"},
notificationClass = AttributeChangeNotification.class,
description = @Description(
value = "description",
bundleBaseName = "bundle",
key = "key"),
descriptorFields = {"foo=bar"})
public static interface ParentIntf {}
public static interface Intf4MBean extends Serializable, ParentIntf, Cloneable {}
public static class Intf4
extends NotificationBroadcasterSupport implements Intf4MBean {}
private static Object mbeanIntf4 = new Intf4();
@NotificationInfo(
types = {"foo", "bar"},
notificationClass = AttributeChangeNotification.class,
description = @Description(
value = "description",
bundleBaseName = "bundle",
key = "key"),
descriptorFields = {"foo=bar"})
public static interface Intf5MXBean {}
public static class Intf5Impl
extends NotificationBroadcasterSupport implements Intf5MXBean {}
private static Object mbeanIntf5 = new Intf5Impl();
public static interface Impl1MBean {}
@NotificationInfo(
types = {"foo", "bar"},
notificationClass = AttributeChangeNotification.class,
description = @Description(
value = "description",
bundleBaseName = "bundle",
key = "key"),
descriptorFields = {"foo=bar"})
public static class Impl1
extends NotificationBroadcasterSupport implements Impl1MBean {}
private static Object mbeanImpl1 = new Impl1();
@NotificationInfo(
types = {"foo", "bar"},
notificationClass = AttributeChangeNotification.class,
description = @Description(
value = "description",
bundleBaseName = "bundle",
key = "key"),
descriptorFields = {"foo=bar"})
public static class ParentImpl extends NotificationBroadcasterSupport {}
public static interface Impl2MBean {}
public static class Impl2 extends ParentImpl implements Impl2MBean {}
private static Object mbeanImpl2 = new Impl2();
public static interface Impl3MXBean {}
@NotificationInfo(
types = {"foo", "bar"},
notificationClass = AttributeChangeNotification.class,
description = @Description(
value = "description",
bundleBaseName = "bundle",
key = "key"),
descriptorFields = {"foo=bar"})
public static class Impl3
extends NotificationBroadcasterSupport implements Impl3MXBean {}
private static Object mbeanImpl3 = new Impl3();
public static class Impl4 extends ParentImpl implements Impl3MXBean {}
private static Object mbeanImpl4 = new Impl4();
@MBean
@NotificationInfo(
types = {"foo", "bar"},
notificationClass = AttributeChangeNotification.class,
description = @Description(
value = "description",
bundleBaseName = "bundle",
key = "key"),
descriptorFields = {"foo=bar"})
public static class MBean1 extends NotificationBroadcasterSupport {}
private static Object mbeanMBean1 = new MBean1();
@MBean
public static class MBean2 extends ParentImpl {}
private static Object mbeanMBean2 = new MBean2();
// Following disabled until we support it
// @MBean
// @NotificationInfo(
// types = {"foo", "bar"},
// notificationClass = AttributeChangeNotification.class,
// description = @Description(
// value = "description",
// bundleBaseName = "bundle",
// key = "key"),
// descriptorFields = {"foo=bar"})
// public static class MBean3 {
// @Resource
// private volatile SendNotification send;
// }
//
// private static Object mbeanMBean3 = new MBean3();
@MXBean
@NotificationInfo(
types = {"foo", "bar"},
notificationClass = AttributeChangeNotification.class,
description = @Description(
value = "description",
bundleBaseName = "bundle",
key = "key"),
descriptorFields = {"foo=bar"})
public static class MXBean1 extends NotificationBroadcasterSupport {}
private static Object mbeanMXBean1 = new MXBean1();
@MXBean
public static class MXBean2 extends ParentImpl {}
private static Object mbeanMXBean2 = new MXBean2();
public static void main(String[] args) throws Exception {
if (!AnnotatedNotificationInfoTest.class.desiredAssertionStatus())
throw new Exception("Test must be run with -ea");
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName on = new ObjectName("a:b=c");
Descriptor expectedDescriptor = new ImmutableDescriptor(
"foo=bar", "descriptionResourceBundleBaseName=bundle",
"descriptionResourceKey=key");
MBeanNotificationInfo expected = new MBeanNotificationInfo(
new String[] {"foo", "bar"},
AttributeChangeNotification.class.getName(),
"description",
expectedDescriptor);
System.out.println("Testing MBeans...");
for (Field mbeanField :
AnnotatedNotificationInfoTest.class.getDeclaredFields()) {
if (!mbeanField.getName().startsWith("mbean"))
continue;
System.out.println("..." + mbeanField.getName());
Object mbean = mbeanField.get(null);
mbs.registerMBean(mbean, on);
MBeanInfo mbi = mbs.getMBeanInfo(on);
MBeanNotificationInfo[] mbnis = mbi.getNotifications();
assert mbnis.length == 1 : mbnis.length;
assert mbnis[0].equals(expected) : mbnis[0];
mbs.unregisterMBean(on);
}
}
}

View File

@ -0,0 +1,830 @@
/*
* 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 6323980
* @summary Test &#64;Description
* @author Eamonn McManus
*/
import java.lang.management.ManagementFactory;
import javax.management.Description;
import javax.management.IntrospectionException;
import javax.management.MBean;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanFeatureInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServer;
import javax.management.MXBean;
import javax.management.ManagedAttribute;
import javax.management.ManagedOperation;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.StandardMBean;
public class MBeanDescriptionTest {
private static String failure;
private static final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
private static final ObjectName name;
static {
try {
name = new ObjectName("a:b=c");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static interface Interface {
@Description("A description")
public String getA();
@Description("B description")
public int getB();
public void setB(int x);
public boolean isC();
@Description("C description")
public void setC(boolean x);
@Description("D description")
public void setD(float x);
@Description("H description")
public int getH();
@Description("H description")
public void setH(int x);
public String getE();
public int getF();
public void setF(int x);
public void setG(boolean x);
@Description("opA description")
public int opA(
@Description("p1 description")
int p1,
@Description("p2 description")
int p2);
public void opB(float x);
}
@Description("MBean description")
public static interface TestMBean extends Interface {}
public static class Test implements TestMBean {
@Description("0-arg constructor description")
public Test() {}
public Test(String why) {}
@Description("2-arg constructor description")
public Test(
@Description("p1 description")
int x,
@Description("p2 description")
String y) {
}
public String getA() {
return null;
}
public int getB() {
return 0;
}
public void setB(int x) {
}
public boolean isC() {
return false;
}
public void setC(boolean x) {
}
public void setD(float x) {
}
public String getE() {
return null;
}
public int getF() {
return 0;
}
public void setF(int x) {
}
public void setG(boolean x) {
}
public int getH() {
return 0;
}
public void setH(int x) {
}
public int opA(int p1, int p2) {
return 0;
}
public void opB(float x) {
}
}
public static class TestSub extends Test {
@Description("0-arg constructor description")
public TestSub() {}
public TestSub(String why) {}
@Description("2-arg constructor description")
public TestSub(
@Description("p1 description")
int x,
@Description("p2 description")
String y) {
}
}
public static class StandardSub extends StandardMBean implements TestMBean {
@Description("0-arg constructor description")
public StandardSub() {
super(TestMBean.class, false);
}
public StandardSub(String why) {
super(TestMBean.class, false);
}
@Description("2-arg constructor description")
public StandardSub(
@Description("p1 description")
int x,
@Description("p2 description")
String y) {
super(TestMBean.class, false);
}
public String getA() {
return null;
}
public int getB() {
return 0;
}
public void setB(int x) {
}
public boolean isC() {
return false;
}
public void setC(boolean x) {
}
public void setD(float x) {
}
public String getE() {
return null;
}
public int getF() {
return 0;
}
public void setF(int x) {
}
public void setG(boolean x) {
}
public int opA(int p1, int p2) {
return 0;
}
public void opB(float x) {
}
public int getH() {
return 0;
}
public void setH(int x) {
}
}
@Description("MBean description")
public static interface TestMXBean extends Interface {}
public static class TestMXBeanImpl implements TestMXBean {
@Description("0-arg constructor description")
public TestMXBeanImpl() {}
public TestMXBeanImpl(String why) {}
@Description("2-arg constructor description")
public TestMXBeanImpl(
@Description("p1 description")
int x,
@Description("p2 description")
String y) {
}
public String getA() {
return null;
}
public int getB() {
return 0;
}
public void setB(int x) {
}
public boolean isC() {
return false;
}
public void setC(boolean x) {
}
public void setD(float x) {
}
public String getE() {
return null;
}
public int getF() {
return 0;
}
public void setF(int x) {
}
public void setG(boolean x) {
}
public int opA(int p1, int p2) {
return 0;
}
public void opB(float x) {
}
public int getH() {
return 0;
}
public void setH(int x) {
}
}
public static class StandardMXSub extends StandardMBean implements TestMXBean {
@Description("0-arg constructor description")
public StandardMXSub() {
super(TestMXBean.class, true);
}
public StandardMXSub(String why) {
super(TestMXBean.class, true);
}
@Description("2-arg constructor description")
public StandardMXSub(
@Description("p1 description")
int x,
@Description("p2 description")
String y) {
super(TestMXBean.class, true);
}
public String getA() {
return null;
}
public int getB() {
return 0;
}
public void setB(int x) {
}
public boolean isC() {
return false;
}
public void setC(boolean x) {
}
public void setD(float x) {
}
public String getE() {
return null;
}
public int getF() {
return 0;
}
public void setF(int x) {
}
public void setG(boolean x) {
}
public int opA(int p1, int p2) {
return 0;
}
public void opB(float x) {
}
public int getH() {
return 0;
}
public void setH(int x) {
}
}
@MBean
@Description("MBean description")
public static class AnnotatedMBean {
@Description("0-arg constructor description")
public AnnotatedMBean() {}
public AnnotatedMBean(String why) {}
@Description("2-arg constructor description")
public AnnotatedMBean(
@Description("p1 description")
int x,
@Description("p2 description")
String y) {}
@ManagedAttribute
@Description("A description")
public String getA() {
return null;
}
@ManagedAttribute
@Description("B description")
public int getB() {
return 0;
}
@ManagedAttribute
public void setB(int x) {
}
@ManagedAttribute
public boolean isC() {
return false;
}
@ManagedAttribute
@Description("C description")
public void setC(boolean x) {
}
@ManagedAttribute
@Description("D description")
public void setD(float x) {
}
@ManagedAttribute
public String getE() {
return null;
}
@ManagedAttribute
public int getF() {
return 0;
}
@ManagedAttribute
public void setF(int x) {
}
@ManagedAttribute
public void setG(boolean x) {
}
@ManagedAttribute
@Description("H description")
public int getH() {
return 0;
}
@ManagedAttribute
@Description("H description")
public void setH(int x) {
}
@ManagedOperation
@Description("opA description")
public int opA(
@Description("p1 description") int p1,
@Description("p2 description") int p2) {
return 0;
}
@ManagedOperation
public void opB(float x) {
}
}
@MXBean
@Description("MBean description")
public static class AnnotatedMXBean {
@Description("0-arg constructor description")
public AnnotatedMXBean() {}
public AnnotatedMXBean(String why) {}
@Description("2-arg constructor description")
public AnnotatedMXBean(
@Description("p1 description")
int x,
@Description("p2 description")
String y) {}
@ManagedAttribute
@Description("A description")
public String getA() {
return null;
}
@ManagedAttribute
@Description("B description")
public int getB() {
return 0;
}
@ManagedAttribute
public void setB(int x) {
}
@ManagedAttribute
public boolean isC() {
return false;
}
@ManagedAttribute
@Description("C description")
public void setC(boolean x) {
}
@ManagedAttribute
@Description("D description")
public void setD(float x) {
}
@ManagedAttribute
public String getE() {
return null;
}
@ManagedAttribute
public int getF() {
return 0;
}
@ManagedAttribute
public void setF(int x) {
}
@ManagedAttribute
public void setG(boolean x) {
}
@ManagedAttribute
@Description("H description")
public int getH() {
return 0;
}
@ManagedAttribute
@Description("H description")
public void setH(int x) {
}
@ManagedOperation
@Description("opA description")
public int opA(
@Description("p1 description") int p1,
@Description("p2 description") int p2) {
return 0;
}
@ManagedOperation
public void opB(float x) {
}
}
// Negative tests follow.
// Inconsistent descriptions
public static interface BadInterface {
@Description("foo")
public String getFoo();
@Description("bar")
public void setFoo(String x);
}
public static interface BadMBean extends BadInterface {}
public static class Bad implements BadMBean {
public String getFoo() {
return null;
}
public void setFoo(String x) {
}
}
public static interface BadMXBean extends BadInterface {}
public static class BadMXBeanImpl implements BadMXBean {
public String getFoo() {
return null;
}
public void setFoo(String x) {
}
}
private static interface Defaults {
public String defaultAttributeDescription(String name);
public String defaultOperationDescription(String name);
public String defaultParameterDescription(int index);
}
private static class StandardDefaults implements Defaults {
public String defaultAttributeDescription(String name) {
return "Attribute exposed for management";
}
public String defaultOperationDescription(String name) {
return "Operation exposed for management";
}
public String defaultParameterDescription(int index) {
return "";
}
}
private static final Defaults standardDefaults = new StandardDefaults();
private static class MXBeanDefaults implements Defaults {
public String defaultAttributeDescription(String name) {
return name;
}
public String defaultOperationDescription(String name) {
return name;
}
public String defaultParameterDescription(int index) {
return "p" + index;
}
}
private static final Defaults mxbeanDefaults = new MXBeanDefaults();
private static class TestCase {
final String name;
final Object mbean;
final Defaults defaults;
TestCase(String name, Object mbean, Defaults defaults) {
this.name = name;
this.mbean = mbean;
this.defaults = defaults;
}
}
private static class ExceptionTest {
final String name;
final Object mbean;
ExceptionTest(String name, Object mbean) {
this.name = name;
this.mbean = mbean;
}
}
private static final TestCase[] tests = {
new TestCase("Standard MBean", new Test(), standardDefaults),
new TestCase("Standard MBean subclass", new TestSub(), standardDefaults),
new TestCase("StandardMBean delegating",
new StandardMBean(new Test(), TestMBean.class, false),
standardDefaults),
new TestCase("StandardMBean delegating to subclass",
new StandardMBean(new TestSub(), TestMBean.class, false),
standardDefaults),
new TestCase("StandardMBean subclass", new StandardSub(), standardDefaults),
new TestCase("MXBean", new TestMXBeanImpl(), mxbeanDefaults),
new TestCase("StandardMBean MXBean delegating",
new StandardMBean(new TestMXBeanImpl(), TestMXBean.class, true),
mxbeanDefaults),
new TestCase("StandardMBean MXBean subclass",
new StandardMXSub(), mxbeanDefaults),
new TestCase("@MBean", new AnnotatedMBean(), standardDefaults),
new TestCase("@MXBean", new AnnotatedMXBean(), mxbeanDefaults),
new TestCase("StandardMBean @MBean delegating",
new StandardMBean(new AnnotatedMBean(), null, false),
standardDefaults),
new TestCase("StandardMBean @MXBean delegating",
new StandardMBean(new AnnotatedMXBean(), null, true),
mxbeanDefaults),
};
private static final ExceptionTest[] exceptionTests = {
new ExceptionTest("Standard MBean with inconsistent get/set", new Bad()),
new ExceptionTest("MXBean with inconsistent get/set", new BadMXBeanImpl()),
};
public static void main(String[] args) throws Exception {
System.out.println("=== Testing correct MBeans ===");
for (TestCase test : tests) {
System.out.println("Testing " + test.name + "...");
mbs.registerMBean(test.mbean, name);
boolean expectConstructors =
(test.mbean.getClass() != StandardMBean.class);
check(mbs.getMBeanInfo(name), test.defaults, expectConstructors);
mbs.unregisterMBean(name);
}
System.out.println();
System.out.println("=== Testing incorrect MBeans ===");
for (ExceptionTest test : exceptionTests) {
System.out.println("Testing " + test.name);
try {
mbs.registerMBean(test.mbean, name);
fail("Registration succeeded but should not have");
mbs.unregisterMBean(name);
} catch (NotCompliantMBeanException e) {
// OK
} catch (Exception e) {
fail("Registration failed with wrong exception: " +
"expected NotCompliantMBeanException, got " +
e.getClass().getName());
}
}
System.out.println();
if (failure == null)
System.out.println("TEST PASSED");
else
throw new Exception("TEST FAILED: " + failure);
}
private static void check(
MBeanInfo mbi, Defaults defaults, boolean expectConstructors)
throws Exception {
assertEquals("MBean description", mbi.getDescription());
// These attributes have descriptions
for (String attr : new String[] {"A", "B", "C", "D", "H"}) {
MBeanAttributeInfo mbai = getAttributeInfo(mbi, attr);
assertEquals(attr + " description", mbai.getDescription());
}
// These attributes don't have descriptions
for (String attr : new String[] {"E", "F", "G"}) {
// If we ever change the default description, we'll need to change
// this test accordingly.
MBeanAttributeInfo mbai = getAttributeInfo(mbi, attr);
assertEquals(
defaults.defaultAttributeDescription(attr), mbai.getDescription());
}
// This operation has a description, as do its parameters
MBeanOperationInfo opA = getOperationInfo(mbi, "opA");
assertEquals("opA description", opA.getDescription());
checkSignature(opA.getSignature());
// This operation has the default description, as does its parameter
MBeanOperationInfo opB = getOperationInfo(mbi, "opB");
assertEquals(defaults.defaultOperationDescription("opB"), opB.getDescription());
MBeanParameterInfo opB0 = opB.getSignature()[0];
assertEquals(defaults.defaultParameterDescription(0), opB0.getDescription());
if (expectConstructors) {
// The 0-arg and 2-arg constructors have descriptions
MBeanConstructorInfo con0 = getConstructorInfo(mbi, 0);
assertEquals("0-arg constructor description", con0.getDescription());
MBeanConstructorInfo con2 = getConstructorInfo(mbi, 2);
assertEquals("2-arg constructor description", con2.getDescription());
checkSignature(con2.getSignature());
// The 1-arg constructor does not have a description.
// The default description for constructors and their
// parameters is the same for all types of MBean.
MBeanConstructorInfo con1 = getConstructorInfo(mbi, 1);
assertEquals("Public constructor of the MBean", con1.getDescription());
assertEquals("", con1.getSignature()[0].getDescription());
}
}
private static void checkSignature(MBeanParameterInfo[] params) {
for (int i = 0; i < params.length; i++) {
MBeanParameterInfo mbpi = params[i];
assertEquals("p" + (i+1) + " description", mbpi.getDescription());
}
}
private static MBeanAttributeInfo getAttributeInfo(MBeanInfo mbi, String attr)
throws Exception {
return getFeatureInfo(mbi.getAttributes(), attr);
}
private static MBeanOperationInfo getOperationInfo(MBeanInfo mbi, String op)
throws Exception {
return getFeatureInfo(mbi.getOperations(), op);
}
private static MBeanConstructorInfo getConstructorInfo(MBeanInfo mbi, int nparams)
throws Exception {
for (MBeanConstructorInfo mbci : mbi.getConstructors()) {
if (mbci.getSignature().length == nparams)
return mbci;
}
throw new Exception("Constructor not found: " + nparams);
}
private static <T extends MBeanFeatureInfo> T getFeatureInfo(
T[] features, String name) throws Exception {
for (T feature : features) {
if (feature.getName().equals(name))
return feature;
}
throw new Exception("Feature not found: " + name);
}
private static void assertEquals(Object expected, Object actual) {
if (!expected.equals(actual))
fail("Expected " + string(expected) + ", got " + string(actual));
}
private static String string(Object x) {
if (x instanceof String)
return quote((String) x);
else
return String.valueOf(x);
}
private static String quote(String s) {
return '"' + s.replace("\\", "\\\\").replace("\"", "\\\"") + '"';
}
private static void fail(String why) {
StackTraceElement[] stack = new Throwable().getStackTrace();
int n = 0;
for (StackTraceElement elmt : stack) {
String method = elmt.getMethodName();
if (method.equals("fail") || method.equals("assertEquals") ||
method.equals("checkSignature"))
continue;
n = elmt.getLineNumber();
break;
}
System.out.println("FAILED: " + why + " (line " + n + ")");
failure = why;
}
}

View File

@ -0,0 +1,116 @@
/*
* 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 6323980
* @summary Test that parameter names can be specified with &#64;Name.
* @author Eamonn McManus
*/
import javax.management.MBean;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MXBean;
import javax.management.ObjectName;
import annot.Name;
import javax.management.ManagedOperation;
public class ParameterNameTest {
public static interface NoddyMBean {
public int add(int x, @Name("y") int y);
}
public static class Noddy implements NoddyMBean {
public int add(int x, int y) {
return x + y;
}
}
public static interface NoddyMXBean {
public int add(int x, @Name("y") int y);
}
public static class NoddyImpl implements NoddyMXBean {
public int add(int x, int y) {
return x + y;
}
}
@MBean
public static class NoddyAnnot {
@ManagedOperation
public int add(int x, @Name("y") int y) {
return x + y;
}
}
@MXBean
public static class NoddyAnnotMX {
@ManagedOperation
public int add(int x, @Name("y") int y) {
return x + y;
}
}
private static final Object[] mbeans = {
new Noddy(), new NoddyImpl(), new NoddyAnnot(), new NoddyAnnotMX(),
};
public static void main(String[] args) throws Exception {
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
ObjectName name = new ObjectName("a:b=c");
for (Object mbean : mbeans) {
System.out.println("Testing " + mbean.getClass().getName());
mbs.registerMBean(mbean, name);
MBeanInfo mbi = mbs.getMBeanInfo(name);
MBeanOperationInfo[] mbois = mbi.getOperations();
assertEquals(1, mbois.length);
MBeanParameterInfo[] mbpis = mbois[0].getSignature();
assertEquals(2, mbpis.length);
boolean mx = Boolean.parseBoolean(
(String) mbi.getDescriptor().getFieldValue("mxbean"));
assertEquals(mx ? "p0" : "p1", mbpis[0].getName());
assertEquals("y", mbpis[1].getName());
mbs.unregisterMBean(name);
}
System.out.println("TEST PASSED");
}
private static void assertEquals(Object expect, Object actual)
throws Exception {
boolean eq;
if (expect == null)
eq = (actual == null);
else
eq = expect.equals(actual);
if (!eq) {
throw new Exception(
"TEST FAILED: expected " + expect + ", found " + actual);
}
}
}

View File

@ -0,0 +1,656 @@
/*
* 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 6323980
* @summary Test resource injection via &#64;Resource
* @author Eamonn McManus
* @run main/othervm -ea ResourceInjectionTest
*/
import java.io.File;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import javax.annotation.Resource;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InstanceNotFoundException;
import javax.management.MBean;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MXBean;
import javax.management.MalformedObjectNameException;
import javax.management.ManagedAttribute;
import javax.management.ManagedOperation;
import javax.management.NotCompliantMBeanException;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.SendNotification;
import javax.management.StandardEmitterMBean;
import javax.management.StandardMBean;
import javax.management.openmbean.MXBeanMappingFactory;
public class ResourceInjectionTest {
private static MBeanServer mbs;
private static final ObjectName objectName;
static {
try {
objectName = new ObjectName("test:type=Test");
} catch (MalformedObjectNameException e) {
throw new RuntimeException(e);
}
}
/* This is somewhat nasty. In the current state of affairs, a
* StandardEmitterMBean can only get the
* MBeanServer to rewrite the source of a Notification from
* the originating object's reference to its ObjectName IF
* StandardEmitterMBean.getResource() returns a reference to the
* wrapped object. By default it doesn't, and you need to specify
* the option below to make it do so. We may hope that this is
* obscure enough for users to run into it rarely if ever.
*/
private static final StandardMBean.Options withWrappedVisible;
private static final StandardMBean.Options withWrappedVisibleMX;
static {
withWrappedVisible = new StandardMBean.Options();
withWrappedVisible.setWrappedObjectVisible(true);
withWrappedVisibleMX = withWrappedVisible.clone();
withWrappedVisibleMX.setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT);
}
@Retention(RetentionPolicy.RUNTIME)
private static @interface ExpectException {
Class<? extends Exception> value();
}
public static void main(String[] args) throws Exception {
if (!ResourceInjectionTest.class.desiredAssertionStatus())
throw new Exception("Test must be run with -ea");
File policyFile = File.createTempFile("jmxperms", ".policy");
policyFile.deleteOnExit();
PrintWriter pw = new PrintWriter(policyFile);
pw.println("grant {");
pw.println(" permission javax.management.MBeanPermission \"*\", \"*\";");
pw.println(" permission javax.management.MBeanServerPermission \"*\";");
pw.println(" permission javax.management.MBeanTrustPermission \"*\";");
pw.println("};");
pw.close();
System.setProperty("java.security.policy", policyFile.getAbsolutePath());
System.setSecurityManager(new SecurityManager());
String failure = null;
for (Method m : ResourceInjectionTest.class.getDeclaredMethods()) {
if (Modifier.isStatic(m.getModifiers()) &&
m.getName().startsWith("test") &&
m.getParameterTypes().length == 0) {
ExpectException expexc = m.getAnnotation(ExpectException.class);
mbs = MBeanServerFactory.newMBeanServer();
try {
m.invoke(null);
if (expexc != null) {
failure =
m.getName() + " did not got expected exception " +
expexc.value().getName();
System.out.println(failure);
} else
System.out.println(m.getName() + " OK");
} catch (InvocationTargetException ite) {
Throwable t = ite.getCause();
String prob = null;
if (expexc != null) {
if (expexc.value().isInstance(t)) {
System.out.println(m.getName() + " OK (got expected " +
expexc.value().getName() + ")");
} else
prob = "got wrong exception";
} else
prob = "got exception";
if (prob != null) {
failure = m.getName() + ": " + prob + " " +
t.getClass().getName();
System.out.println(failure);
t.printStackTrace(System.out);
}
}
}
}
if (failure == null)
System.out.println("TEST PASSED");
else
throw new Exception("TEST FAILED: " + failure);
}
private static interface Send {
public void send();
}
// Test @Resource in MBean defined by annotations
@MBean
public static class Annotated {
@Resource
private volatile MBeanServer mbeanServer;
@Resource
private volatile ObjectName myName;
@ManagedAttribute
public ObjectName getMyName() {
return myName;
}
@ManagedOperation
public void unregisterSelf()
throws InstanceNotFoundException, MBeanRegistrationException {
mbeanServer.unregisterMBean(myName);
}
}
private static void testAnnotated() throws Exception {
testMBean(new Annotated());
}
private static void testAnnotatedWrapped() throws Exception {
testMBean(new StandardMBean(new Annotated(), null));
}
@MBean
public static class AnnotatedSend extends Annotated implements Send {
@Resource
private volatile SendNotification sender;
@ManagedOperation
public void send() {
sender.sendNotification(new Notification("type", this, 0L));
}
}
private static void testAnnotatedSend() throws Exception {
testMBean(new AnnotatedSend());
}
private static void testAnnotatedSendWrapped() throws Exception {
testMBean(new StandardEmitterMBean(
new AnnotatedSend(), null, withWrappedVisible, null));
}
// Test @Resource in MXBean defined by annotations
@MXBean
public static class AnnotatedMX {
@Resource
private volatile MBeanServer mbeanServer;
@Resource
private volatile ObjectName myName;
@ManagedAttribute
public ObjectName getMyName() {
return myName;
}
@ManagedOperation
public void unregisterSelf()
throws InstanceNotFoundException, MBeanRegistrationException {
mbeanServer.unregisterMBean(myName);
}
}
private static void testAnnotatedMX() throws Exception {
testMBean(new AnnotatedMX());
}
private static void testAnnotatedMXWrapped() throws Exception {
testMBean(new StandardMBean(new AnnotatedMX(), null, true));
}
public static class AnnotatedMXSend extends AnnotatedMX implements Send {
@Resource
private volatile SendNotification sender;
@ManagedOperation
public void send() {
sender.sendNotification(new Notification("type", this, 0L));
}
}
private static void testAnnotatedMXSend() throws Exception {
testMBean(new AnnotatedMXSend());
}
private static void testAnnotatedMXSendWrapped() throws Exception {
testMBean(new StandardEmitterMBean(
new AnnotatedMXSend(), null, withWrappedVisibleMX, null));
}
// Test @Resource in Standard MBean
public static interface SimpleStandardMBean {
public ObjectName getMyName();
public void unregisterSelf() throws Exception;
}
public static class SimpleStandard implements SimpleStandardMBean {
@Resource(type = MBeanServer.class)
private volatile Object mbeanServer;
@Resource(type = ObjectName.class)
private volatile Object myName;
public ObjectName getMyName() {
return (ObjectName) myName;
}
public void unregisterSelf() throws Exception {
((MBeanServer) mbeanServer).unregisterMBean(getMyName());
}
}
private static void testStandard() throws Exception {
testMBean(new SimpleStandard());
}
private static void testStandardWrapped() throws Exception {
testMBean(new StandardMBean(new SimpleStandard(), SimpleStandardMBean.class));
}
public static interface SimpleStandardSendMBean extends SimpleStandardMBean {
public void send();
}
public static class SimpleStandardSend
extends SimpleStandard implements SimpleStandardSendMBean {
@Resource(type = SendNotification.class)
private volatile Object sender;
public void send() {
((SendNotification) sender).sendNotification(
new Notification("type", this, 0L));
}
}
private static void testStandardSend() throws Exception {
testMBean(new SimpleStandardSend());
}
private static void testStandardSendWrapped() throws Exception {
testMBean(new StandardEmitterMBean(
new SimpleStandardSend(), SimpleStandardSendMBean.class,
withWrappedVisible, null));
}
// Test @Resource in MXBean
public static interface SimpleMXBean {
public ObjectName getMyName();
public void unregisterSelf() throws Exception;
}
public static class SimpleMX implements SimpleMXBean {
@Resource(type = MBeanServer.class)
private volatile Object mbeanServer;
@Resource(type = ObjectName.class)
private volatile Object myName;
public ObjectName getMyName() {
return (ObjectName) myName;
}
public void unregisterSelf() throws Exception {
((MBeanServer) mbeanServer).unregisterMBean(getMyName());
}
}
private static void testMX() throws Exception {
testMBean(new SimpleMX());
}
private static void testMXWrapped() throws Exception {
testMBean(new StandardMBean(new SimpleMX(), SimpleMXBean.class, true));
}
public static interface SimpleMXBeanSend extends SimpleMXBean {
public void send();
}
public MBeanServer getMbs() {
return mbs;
}
public static class SimpleMXSend extends SimpleMX implements SimpleMXBeanSend {
@Resource(type = SendNotification.class)
private volatile Object sender;
public void send() {
((SendNotification) sender).sendNotification(
new Notification("type", this, 0L));
}
}
private static void testMXSend() throws Exception {
testMBean(new SimpleMXSend());
}
private static void testMXSendWrapped() throws Exception {
testMBean(new StandardEmitterMBean(
new SimpleMXSend(), SimpleMXBeanSend.class,
withWrappedVisibleMX, null));
}
// Test @Resource in Dynamic MBean
private static class SimpleDynamic implements DynamicMBean {
private MBeanServer mbeanServer;
private ObjectName myName;
@Resource
private synchronized void setMBeanServer(MBeanServer mbs) {
mbeanServer = mbs;
}
@Resource(type = ObjectName.class)
private synchronized void setObjectName(Serializable name) {
myName = (ObjectName) name;
}
public synchronized Object getAttribute(String attribute)
throws AttributeNotFoundException {
if (attribute.equals("MyName"))
return myName;
throw new AttributeNotFoundException(attribute);
}
public void setAttribute(Attribute attribute)
throws AttributeNotFoundException {
throw new AttributeNotFoundException(attribute.getName());
}
public synchronized AttributeList getAttributes(String[] attributes) {
AttributeList list = new AttributeList();
for (String name : attributes) {
if (name.equals("MyName"))
list.add(new Attribute("MyName", myName));
}
return list;
}
public AttributeList setAttributes(AttributeList attributes) {
return new AttributeList();
}
public synchronized Object invoke(
String actionName, Object[] params, String[] signature)
throws MBeanException, ReflectionException {
if (actionName.equals("unregisterSelf") &&
(params == null || params.length == 0) &&
(signature == null || signature.length == 0)) {
try {
mbeanServer.unregisterMBean(myName);
return null;
} catch (Exception x) {
throw new MBeanException(x);
}
} else {
Exception x = new NoSuchMethodException(
actionName + Arrays.toString(signature));
throw new MBeanException(x);
}
}
public MBeanInfo getMBeanInfo() {
DynamicMBean mbean = new StandardMBean(
new SimpleStandard(), SimpleStandardMBean.class, false);
return mbean.getMBeanInfo();
}
}
private static void testDynamic() throws Exception {
testMBean(new SimpleDynamic());
}
private static class SimpleDynamicSend extends SimpleDynamic {
private SendNotification sender;
@Resource
private synchronized void setSender(SendNotification sender) {
this.sender = sender;
}
@Override
public synchronized Object invoke(
String actionName, Object[] params, String[] signature)
throws MBeanException, ReflectionException {
if (actionName.equals("send")) {
sender.sendNotification(new Notification("type", this, 0L));
return null;
} else
return super.invoke(actionName, params, signature);
}
}
private static void testDynamicSend() throws Exception {
testMBean(new SimpleDynamicSend());
}
// Test that @Resource classes don't have to be public
// They can even be defined within methods!
// But you can't have any @ManagedAttributes or @ManagedOperations
// in such MBeans so their utility is limited.
private static void testNonPublic() throws Exception {
@MBean
class NonPublic {
@Resource
ObjectName myName;
}
assert !Modifier.isPublic(NonPublic.class.getModifiers());
NonPublic mbean = new NonPublic();
mbs.registerMBean(mbean, objectName);
assert objectName.equals(mbean.myName);
}
// Test inheritance and multiple injections of the same value
private static class ManyResources extends AnnotatedSend {
@Resource
private volatile ObjectName myName; // same name as in parent!
@Resource(type=ObjectName.class)
private volatile Object myOtherName;
private volatile ObjectName myThirdName;
private volatile ObjectName myFourthName;
private volatile int methodCalls;
@Resource
private volatile SendNotification send1;
@Resource(type = SendNotification.class)
private volatile Object send2;
@Resource
void setMyName(ObjectName name) {
myThirdName = name;
methodCalls++;
}
@Resource(type=ObjectName.class)
private void setMyNameAgain(ObjectName name) {
myFourthName = name;
methodCalls++;
}
void check() {
assert objectName.equals(myName) : myName;
for (ObjectName name : new ObjectName[] {
(ObjectName)myOtherName, myThirdName, myFourthName
}) {
assert myName == name : name;
}
assert methodCalls == 2 : methodCalls;
assert send1 != null && send2 == send1;
}
}
private static void testManyResources() throws Exception {
ManyResources mr = new ManyResources();
testMBean(mr);
mr.check();
}
// Test that method override doesn't lead to multiple calls of the same method
private static class ManyResourcesSub extends ManyResources {
private boolean called;
@Override
@Resource
void setMyName(ObjectName name) {
super.setMyName(name);
called = true;
}
void check2() {
assert called;
}
}
private static void testOverride() throws Exception {
ManyResourcesSub mrs = new ManyResourcesSub();
testMBean(mrs);
mrs.check();
mrs.check2();
}
// Test that @Resource is illegal on static fields
@MBean
public static class StaticResource {
@Resource
private static ObjectName name;
}
@ExpectException(NotCompliantMBeanException.class)
private static void testStaticResource() throws Exception {
testMBean(new StaticResource());
}
// Test that @Resource is illegal on static methods
@MBean
public static class StaticResourceMethod {
@Resource
private static void setObjectName(ObjectName name) {}
}
@ExpectException(NotCompliantMBeanException.class)
private static void testStaticResourceMethod() throws Exception {
testMBean(new StaticResourceMethod());
}
// Test that @Resource is illegal on methods that don't return void
@MBean
public static class NonVoidMethod {
@Resource
private String setObjectName(ObjectName name) {
return "oops";
}
}
@ExpectException(NotCompliantMBeanException.class)
private static void testNonVoidMethod() throws Exception {
testMBean(new NonVoidMethod());
}
// Test that @Resource is illegal on methods with no arguments
@MBean
public static class NoArgMethod {
@Resource(type=ObjectName.class)
private void setObjectName() {}
}
@ExpectException(NotCompliantMBeanException.class)
private static void testNoArgMethod() throws Exception {
testMBean(new NoArgMethod());
}
// Test that @Resource is illegal on methods with more than one argument
@MBean
public static class MultiArgMethod {
@Resource
private void setObjectName(ObjectName name, String what) {}
}
@ExpectException(NotCompliantMBeanException.class)
private static void testMultiArgMethod() throws Exception {
testMBean(new MultiArgMethod());
}
private static class CountListener implements NotificationListener {
volatile int count;
public void handleNotification(Notification notification, Object handback) {
count++;
}
}
private static void testMBean(Object mbean) throws Exception {
mbs.registerMBean(mbean, objectName);
final ObjectName name = (ObjectName) mbs.getAttribute(objectName, "MyName");
assert objectName.equals(name) : name;
if (mbean instanceof Send || mbean instanceof NotificationEmitter) {
assert mbs.isInstanceOf(name, NotificationEmitter.class.getName());
CountListener countL = new CountListener();
mbs.addNotificationListener(name, countL, null, null);
NotificationListener checkSource = new NotificationListener() {
public void handleNotification(Notification n, Object h) {
assert n.getSource().equals(name) : n.getSource();
}
};
mbs.addNotificationListener(name, checkSource, null, null);
mbs.invoke(objectName, "send", null, null);
assert countL.count == 1;
mbs.removeNotificationListener(name, checkSource);
mbs.removeNotificationListener(name, countL, null, null);
}
mbs.invoke(objectName, "unregisterSelf", null, null);
assert !mbs.isRegistered(objectName);
}
}

View File

@ -0,0 +1,32 @@
/*
* 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 annot;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Name {
String value();
}