提交 a311994c 编写于 作者: X xdono

Merge

...@@ -5,3 +5,4 @@ fb57027902e04ecafceae31a605e69b436c23d57 jdk7-b26 ...@@ -5,3 +5,4 @@ fb57027902e04ecafceae31a605e69b436c23d57 jdk7-b26
02e4c5348592a8d7fc2cba28bc5f8e35c0e17277 jdk7-b28 02e4c5348592a8d7fc2cba28bc5f8e35c0e17277 jdk7-b28
e21f4266466cd1306b176aaa08b2cd8337a9be3d jdk7-b29 e21f4266466cd1306b176aaa08b2cd8337a9be3d jdk7-b29
b6d6877c1155621a175dccd12dc14c54f938fb8b jdk7-b30 b6d6877c1155621a175dccd12dc14c54f938fb8b jdk7-b30
b7474b739d13bacd9972f88ac91f6350b7b0be12 jdk7-b31
...@@ -492,14 +492,17 @@ eventFilterRestricted_passesFilter(JNIEnv *env, ...@@ -492,14 +492,17 @@ eventFilterRestricted_passesFilter(JNIEnv *env,
char *sourceName = 0; char *sourceName = 0;
jvmtiError error = JVMTI_FUNC_PTR(gdata->jvmti,GetSourceFileName) jvmtiError error = JVMTI_FUNC_PTR(gdata->jvmti,GetSourceFileName)
(gdata->jvmti, clazz, &sourceName); (gdata->jvmti, clazz, &sourceName);
if (error == JVMTI_ERROR_NONE) { if (error == JVMTI_ERROR_NONE &&
if (sourceName == 0 || !patternStringMatch(sourceName, desiredNamePattern)) { sourceName != 0 &&
/* We have no match */ patternStringMatch(sourceName, desiredNamePattern)) {
jvmtiDeallocate(sourceName); // got a hit - report the event
return JNI_FALSE; jvmtiDeallocate(sourceName);
} break;
} }
// We have no match, we have no source file name,
// or we got a JVM TI error. Don't report the event.
jvmtiDeallocate(sourceName); jvmtiDeallocate(sourceName);
return JNI_FALSE;
} }
break; break;
} }
......
...@@ -686,7 +686,7 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { ...@@ -686,7 +686,7 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory {
final String msg = final String msg =
"Cannot convert SortedSet with non-null comparator: " + "Cannot convert SortedSet with non-null comparator: " +
comparator; comparator;
throw new OpenDataException(msg); throw openDataException(msg, new IllegalArgumentException(msg));
} }
} }
final Object[] openArray = (Object[]) final Object[] openArray = (Object[])
...@@ -800,7 +800,7 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { ...@@ -800,7 +800,7 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory {
final String msg = final String msg =
"Cannot convert SortedMap with non-null comparator: " + "Cannot convert SortedMap with non-null comparator: " +
comparator; comparator;
throw new OpenDataException(msg); throw openDataException(msg, new IllegalArgumentException(msg));
} }
} }
final TabularType tabularType = (TabularType) getOpenType(); final TabularType tabularType = (TabularType) getOpenType();
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
package com.sun.jmx.mbeanserver; package com.sun.jmx.mbeanserver;
import javax.management.DynamicMBean; import javax.management.DynamicWrapperMBean;
import javax.management.MBeanServer; import javax.management.MBeanServer;
import javax.management.ObjectName; import javax.management.ObjectName;
...@@ -35,17 +35,7 @@ import javax.management.ObjectName; ...@@ -35,17 +35,7 @@ import javax.management.ObjectName;
* *
* @since 1.6 * @since 1.6
*/ */
public interface DynamicMBean2 extends DynamicMBean { public interface DynamicMBean2 extends DynamicWrapperMBean {
/**
* 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();
/** /**
* The name of this MBean's class, as used by permission checks. * The name of this MBean's class, as used by permission checks.
* This is typically equal to getResource().getClass().getName(). * This is typically equal to getResource().getClass().getName().
......
...@@ -25,23 +25,39 @@ ...@@ -25,23 +25,39 @@
package com.sun.jmx.mbeanserver; 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.annotation.Annotation;
import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException; import java.lang.reflect.UndeclaredThrowableException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import javax.management.AttributeNotFoundException;
import javax.management.Description;
import javax.management.Descriptor; import javax.management.Descriptor;
import javax.management.DescriptorFields;
import javax.management.DescriptorKey; import javax.management.DescriptorKey;
import javax.management.DynamicMBean; import javax.management.DynamicMBean;
import javax.management.ImmutableDescriptor; import javax.management.ImmutableDescriptor;
import javax.management.MBean;
import javax.management.MBeanInfo; import javax.management.MBeanInfo;
import javax.management.MXBean;
import javax.management.NotCompliantMBeanException; 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 com.sun.jmx.remote.util.EnvHelp;
import java.beans.BeanInfo; import java.beans.BeanInfo;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
...@@ -133,8 +149,12 @@ public class Introspector { ...@@ -133,8 +149,12 @@ public class Introspector {
} }
} }
public static void checkCompliance(Class mbeanClass) public static void checkCompliance(Class<?> mbeanClass)
throws NotCompliantMBeanException { throws NotCompliantMBeanException {
// Check that @Resource is used correctly (if it used).
MBeanInjector.validate(mbeanClass);
// Is DynamicMBean? // Is DynamicMBean?
// //
if (DynamicMBean.class.isAssignableFrom(mbeanClass)) if (DynamicMBean.class.isAssignableFrom(mbeanClass))
...@@ -157,21 +177,39 @@ public class Introspector { ...@@ -157,21 +177,39 @@ public class Introspector {
} catch (NotCompliantMBeanException e) { } catch (NotCompliantMBeanException e) {
mxbeanException = 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 = final String msg =
"MBean class " + mbeanClass.getName() + " does not implement " + "MBean class " + mbeanClass.getName() + " does not implement " +
"DynamicMBean, neither follows the Standard MBean conventions (" + "DynamicMBean; does not follow the Standard MBean conventions (" +
mbeanException.toString() + ") nor the MXBean conventions (" + mbeanException.toString() + "); does not follow the MXBean conventions (" +
mxbeanException.toString() + ")"; mxbeanException.toString() + "); and does not have or inherit the @" +
MBean.class.getSimpleName() + " or @" + MXBean.class.getSimpleName() +
" annotation";
throw new NotCompliantMBeanException(msg); 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) public static <T> DynamicMBean makeDynamicMBean(T mbean)
throws NotCompliantMBeanException { throws NotCompliantMBeanException {
if (mbean == null) if (mbean == null)
throw new NotCompliantMBeanException("Null MBean object"); throw new NotCompliantMBeanException("Null MBean object");
if (mbean instanceof DynamicMBean) if (mbean instanceof DynamicMBean)
return (DynamicMBean) mbean; return (DynamicMBean) mbean;
final Class mbeanClass = mbean.getClass(); final Class<?> mbeanClass = mbean.getClass();
Class<? super T> c = null; Class<? super T> c = null;
try { try {
c = Util.cast(getStandardMBeanInterface(mbeanClass)); c = Util.cast(getStandardMBeanInterface(mbeanClass));
...@@ -270,7 +308,7 @@ public class Introspector { ...@@ -270,7 +308,7 @@ public class Introspector {
* Return <code>null</code> if the MBean is a DynamicMBean, * Return <code>null</code> if the MBean is a DynamicMBean,
* or if no MBean interface is found. * 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 // Check if the given class implements the MBean interface
// or the Dynamic MBean interface // or the Dynamic MBean interface
if (isDynamic(baseClass)) return null; if (isDynamic(baseClass)) return null;
...@@ -291,10 +329,12 @@ public class Introspector { ...@@ -291,10 +329,12 @@ public class Introspector {
* @throws NotCompliantMBeanException The specified class is * @throws NotCompliantMBeanException The specified class is
* not a JMX compliant Standard MBean. * not a JMX compliant Standard MBean.
*/ */
public static Class getStandardMBeanInterface(Class baseClass) public static <T> Class<? super T> getStandardMBeanInterface(Class<T> baseClass)
throws NotCompliantMBeanException { throws NotCompliantMBeanException {
Class current = baseClass; if (baseClass.isAnnotationPresent(MBean.class))
Class mbeanInterface = null; return baseClass;
Class<? super T> current = baseClass;
Class<? super T> mbeanInterface = null;
while (current != null) { while (current != null) {
mbeanInterface = mbeanInterface =
findMBeanInterface(current, current.getName()); findMBeanInterface(current, current.getName());
...@@ -321,8 +361,10 @@ public class Introspector { ...@@ -321,8 +361,10 @@ public class Introspector {
* @throws NotCompliantMBeanException The specified class is * @throws NotCompliantMBeanException The specified class is
* not a JMX compliant MXBean. * not a JMX compliant MXBean.
*/ */
public static Class getMXBeanInterface(Class baseClass) public static <T> Class<? super T> getMXBeanInterface(Class<T> baseClass)
throws NotCompliantMBeanException { throws NotCompliantMBeanException {
if (hasMXBeanAnnotation(baseClass))
return baseClass;
try { try {
return MXBeanSupport.findMXBeanInterface(baseClass); return MXBeanSupport.findMXBeanInterface(baseClass);
} catch (Exception e) { } catch (Exception e) {
...@@ -345,19 +387,24 @@ public class Introspector { ...@@ -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 * Try to find the MBean interface corresponding to the class aName
* - i.e. <i>aName</i>MBean, from within aClass and its superclasses. * - i.e. <i>aName</i>MBean, from within aClass and its superclasses.
**/ **/
private static Class findMBeanInterface(Class aClass, String aName) { private static <T> Class<? super T> findMBeanInterface(
Class current = aClass; Class<T> aClass, String aName) {
Class<? super T> current = aClass;
while (current != null) { while (current != null) {
final Class[] interfaces = current.getInterfaces(); final Class<?>[] interfaces = current.getInterfaces();
final int len = interfaces.length; final int len = interfaces.length;
for (int i=0;i<len;i++) { for (int i=0;i<len;i++) {
final Class inter = Class<? super T> inter = Util.cast(interfaces[i]);
implementsMBean(interfaces[i], aName); inter = implementsMBean(inter, aName);
if (inter != null) return inter; if (inter != null) return inter;
} }
current = current.getSuperclass(); current = current.getSuperclass();
...@@ -365,6 +412,48 @@ public class Introspector { ...@@ -365,6 +412,48 @@ public class Introspector {
return null; 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) { public static Descriptor descriptorForElement(final AnnotatedElement elmt) {
if (elmt == null) if (elmt == null)
return ImmutableDescriptor.EMPTY_DESCRIPTOR; return ImmutableDescriptor.EMPTY_DESCRIPTOR;
...@@ -372,41 +461,18 @@ public class Introspector { ...@@ -372,41 +461,18 @@ public class Introspector {
return descriptorForAnnotations(annots); return descriptorForAnnotations(annots);
} }
public static Descriptor descriptorForAnnotation(Annotation annot) {
return descriptorForAnnotations(new Annotation[] {annot});
}
public static Descriptor descriptorForAnnotations(Annotation[] annots) { public static Descriptor descriptorForAnnotations(Annotation[] annots) {
if (annots.length == 0) if (annots.length == 0)
return ImmutableDescriptor.EMPTY_DESCRIPTOR; return ImmutableDescriptor.EMPTY_DESCRIPTOR;
Map<String, Object> descriptorMap = new HashMap<String, Object>(); Map<String, Object> descriptorMap = new HashMap<String, Object>();
for (Annotation a : annots) { for (Annotation a : annots) {
Class<? extends Annotation> c = a.annotationType(); if (a instanceof DescriptorFields)
Method[] elements = c.getMethods(); addDescriptorFieldsToMap(descriptorMap, (DescriptorFields) a);
for (Method element : elements) { addAnnotationFieldsToMap(descriptorMap, a);
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 (descriptorMap.isEmpty()) if (descriptorMap.isEmpty())
...@@ -415,6 +481,62 @@ public class Introspector { ...@@ -415,6 +481,62 @@ public class Introspector {
return new ImmutableDescriptor(descriptorMap); 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. * Throws a NotCompliantMBeanException or a SecurityException.
* @param notCompliant the class which was under examination * @param notCompliant the class which was under examination
...@@ -473,8 +595,13 @@ public class Introspector { ...@@ -473,8 +595,13 @@ public class Introspector {
// The only other possibility is that the value is another // The only other possibility is that the value is another
// annotation, or that the language has evolved since this code // annotation, or that the language has evolved since this code
// was written. We don't allow for either of those currently. // 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 " + 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 // This must be consistent with the check for duplicate field values in
...@@ -490,15 +617,15 @@ public class Introspector { ...@@ -490,15 +617,15 @@ public class Introspector {
* @param c The interface to be tested * @param c The interface to be tested
* @param clName The name of the class implementing this interface * @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"; String clMBeanName = clName + "MBean";
if (c.getName().equals(clMBeanName)) { if (c.getName().equals(clMBeanName)) {
return c; return c;
} }
Class[] interfaces = c.getInterfaces(); Class<?>[] interfaces = c.getInterfaces();
for (int i = 0;i < interfaces.length; i++) { for (int i = 0;i < interfaces.length; i++) {
if (interfaces[i].getName().equals(clMBeanName)) if (interfaces[i].getName().equals(clMBeanName))
return interfaces[i]; return Util.cast(interfaces[i]);
} }
return null; return null;
......
...@@ -33,6 +33,10 @@ import java.util.Comparator; ...@@ -33,6 +33,10 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.management.MBean;
import javax.management.MXBean;
import javax.management.ManagedAttribute;
import javax.management.ManagedOperation;
import javax.management.NotCompliantMBeanException; import javax.management.NotCompliantMBeanException;
/** /**
...@@ -125,18 +129,26 @@ class MBeanAnalyzer<M> { ...@@ -125,18 +129,26 @@ class MBeanAnalyzer<M> {
for (Method m : methods) { for (Method m : methods) {
final String name = m.getName(); final String name = m.getName();
final int nParams = m.getParameterTypes().length; 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); final M cm = introspector.mFrom(m);
String attrName = ""; String attrName = "";
if (name.startsWith("get")) if (!managedOp) {
attrName = name.substring(3); if (name.startsWith("get"))
else if (name.startsWith("is") attrName = name.substring(3);
&& m.getReturnType() == boolean.class) else if (name.startsWith("is")
attrName = name.substring(2); && m.getReturnType() == boolean.class)
attrName = name.substring(2);
}
if (attrName.length() != 0 && nParams == 0 if (attrName.length() != 0 && nParams == 0
&& m.getReturnType() != void.class) { && m.getReturnType() != void.class && !managedOp) {
// It's a getter // It's a getter
// Check we don't have both isX and getX // Check we don't have both isX and getX
AttrMethods<M> am = attrMap.get(attrName); AttrMethods<M> am = attrMap.get(attrName);
...@@ -153,7 +165,7 @@ class MBeanAnalyzer<M> { ...@@ -153,7 +165,7 @@ class MBeanAnalyzer<M> {
attrMap.put(attrName, am); attrMap.put(attrName, am);
} else if (name.startsWith("set") && name.length() > 3 } else if (name.startsWith("set") && name.length() > 3
&& nParams == 1 && && nParams == 1 &&
m.getReturnType() == void.class) { m.getReturnType() == void.class && !managedOp) {
// It's a setter // It's a setter
attrName = name.substring(3); attrName = name.substring(3);
AttrMethods<M> am = attrMap.get(attrName); AttrMethods<M> am = attrMap.get(attrName);
...@@ -166,6 +178,9 @@ class MBeanAnalyzer<M> { ...@@ -166,6 +178,9 @@ class MBeanAnalyzer<M> {
} }
am.setter = cm; am.setter = cm;
attrMap.put(attrName, am); attrMap.put(attrName, am);
} else if (managedAttr) {
throw new NotCompliantMBeanException("Method " + name +
" has @ManagedAttribute but is not a valid getter or setter");
} else { } else {
// It's an operation // It's an operation
List<M> cms = opMap.get(name); List<M> cms = opMap.get(name);
......
/*
* 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>>();
}
...@@ -36,20 +36,28 @@ import java.lang.reflect.Method; ...@@ -36,20 +36,28 @@ import java.lang.reflect.Method;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import javax.management.Description;
import javax.management.Descriptor; import javax.management.Descriptor;
import javax.management.ImmutableDescriptor; import javax.management.ImmutableDescriptor;
import javax.management.IntrospectionException; import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException; import javax.management.InvalidAttributeValueException;
import javax.management.MBean;
import javax.management.MBeanAttributeInfo; import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo; import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException; import javax.management.MBeanException;
import javax.management.MBeanInfo; import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo; import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo; import javax.management.MBeanOperationInfo;
import javax.management.MXBean;
import javax.management.ManagedAttribute;
import javax.management.ManagedOperation;
import javax.management.NotCompliantMBeanException; import javax.management.NotCompliantMBeanException;
import javax.management.NotificationBroadcaster; import javax.management.NotificationBroadcaster;
import javax.management.NotificationInfo;
import javax.management.NotificationInfos;
import javax.management.ReflectionException; import javax.management.ReflectionException;
/** /**
...@@ -153,6 +161,25 @@ abstract class MBeanIntrospector<M> { ...@@ -153,6 +161,25 @@ abstract class MBeanIntrospector<M> {
abstract MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, abstract MBeanAttributeInfo getMBeanAttributeInfo(String attributeName,
M getter, M setter) throws IntrospectionException; 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 * Construct an MBeanOperationInfo for the given operation based on
* the M it was derived from. * the M it was derived from.
...@@ -184,8 +211,12 @@ abstract class MBeanIntrospector<M> { ...@@ -184,8 +211,12 @@ abstract class MBeanIntrospector<M> {
} }
void checkCompliance(Class<?> mbeanType) throws NotCompliantMBeanException { void checkCompliance(Class<?> mbeanType) throws NotCompliantMBeanException {
if (!mbeanType.isInterface()) { if (!mbeanType.isInterface() &&
throw new NotCompliantMBeanException("Not an interface: " + !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()); mbeanType.getName());
} }
} }
...@@ -194,7 +225,12 @@ abstract class MBeanIntrospector<M> { ...@@ -194,7 +225,12 @@ abstract class MBeanIntrospector<M> {
* Get the methods to be analyzed to build the MBean interface. * Get the methods to be analyzed to build the MBean interface.
*/ */
List<Method> getMethods(final Class<?> mbeanType) throws Exception { 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) final PerInterface<M> getPerInterface(Class<?> mbeanInterface)
...@@ -232,8 +268,11 @@ abstract class MBeanIntrospector<M> { ...@@ -232,8 +268,11 @@ abstract class MBeanIntrospector<M> {
MBeanAnalyzer<M> analyzer) throws IntrospectionException { MBeanAnalyzer<M> analyzer) throws IntrospectionException {
final MBeanInfoMaker maker = new MBeanInfoMaker(); final MBeanInfoMaker maker = new MBeanInfoMaker();
analyzer.visit(maker); analyzer.visit(maker);
final String description = final String defaultDescription =
"Information on the management interface of the MBean"; "Information on the management interface of the MBean";
String description = Introspector.descriptionForElement(mbeanInterface);
if (description == null)
description = defaultDescription;
return maker.makeMBeanInfo(mbeanInterface, description); return maker.makeMBeanInfo(mbeanInterface, description);
} }
...@@ -407,7 +446,15 @@ abstract class MBeanIntrospector<M> { ...@@ -407,7 +446,15 @@ abstract class MBeanIntrospector<M> {
throws NotCompliantMBeanException { throws NotCompliantMBeanException {
MBeanInfo mbi = MBeanInfo mbi =
getClassMBeanInfo(resource.getClass(), perInterface); 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(); Descriptor d = getSpecificMBeanDescriptor();
boolean anyNotifs = (notifs != null && notifs.length > 0); boolean anyNotifs = (notifs != null && notifs.length > 0);
if (!anyNotifs && ImmutableDescriptor.EMPTY_DESCRIPTOR.equals(d)) if (!anyNotifs && ImmutableDescriptor.EMPTY_DESCRIPTOR.equals(d))
...@@ -460,13 +507,43 @@ abstract class MBeanIntrospector<M> { ...@@ -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) { static MBeanNotificationInfo[] findNotifications(Object moi) {
if (!(moi instanceof NotificationBroadcaster)) if (!(moi instanceof NotificationBroadcaster))
return null; return null;
MBeanNotificationInfo[] mbn = MBeanNotificationInfo[] mbn =
((NotificationBroadcaster) moi).getNotificationInfo(); ((NotificationBroadcaster) moi).getNotificationInfo();
if (mbn == null || mbn.length == 0) if (mbn == null || mbn.length == 0)
return null; return findNotificationsFromAnnotations(moi.getClass());
MBeanNotificationInfo[] result = MBeanNotificationInfo[] result =
new MBeanNotificationInfo[mbn.length]; new MBeanNotificationInfo[mbn.length];
for (int i = 0; i < mbn.length; i++) { for (int i = 0; i < mbn.length; i++) {
...@@ -478,11 +555,81 @@ abstract class MBeanIntrospector<M> { ...@@ -478,11 +555,81 @@ abstract class MBeanIntrospector<M> {
return result; 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) { private static MBeanConstructorInfo[] findConstructors(Class<?> c) {
Constructor[] cons = c.getConstructors(); Constructor[] cons = c.getConstructors();
MBeanConstructorInfo[] mbc = new MBeanConstructorInfo[cons.length]; MBeanConstructorInfo[] mbc = new MBeanConstructorInfo[cons.length];
for (int i = 0; i < cons.length; i++) { 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]); mbc[i] = new MBeanConstructorInfo(descr, cons[i]);
} }
return mbc; return mbc;
......
...@@ -263,10 +263,14 @@ public abstract class MBeanSupport<M> ...@@ -263,10 +263,14 @@ public abstract class MBeanSupport<M>
return resource.getClass().getName(); return resource.getClass().getName();
} }
public final Object getResource() { public final Object getWrappedObject() {
return resource; return resource;
} }
public final ClassLoader getWrappedClassLoader() {
return resource.getClass().getClassLoader();
}
public final Class<?> getMBeanInterface() { public final Class<?> getMBeanInterface() {
return perInterface.getMBeanInterface(); return perInterface.getMBeanInterface();
} }
......
...@@ -35,6 +35,7 @@ import java.lang.reflect.Method; ...@@ -35,6 +35,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import javax.management.Description;
import javax.management.Descriptor; import javax.management.Descriptor;
import javax.management.ImmutableDescriptor; import javax.management.ImmutableDescriptor;
import javax.management.IntrospectionException; import javax.management.IntrospectionException;
...@@ -43,6 +44,7 @@ import javax.management.MBeanAttributeInfo; ...@@ -43,6 +44,7 @@ import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException; import javax.management.MBeanException;
import javax.management.MBeanOperationInfo; import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo; import javax.management.MBeanParameterInfo;
import javax.management.ManagedOperation;
import javax.management.NotCompliantMBeanException; import javax.management.NotCompliantMBeanException;
import javax.management.openmbean.MXBeanMappingFactory; import javax.management.openmbean.MXBeanMappingFactory;
import javax.management.openmbean.OpenMBeanAttributeInfoSupport; import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
...@@ -180,7 +182,10 @@ class MXBeanIntrospector extends MBeanIntrospector<ConvertingMethod> { ...@@ -180,7 +182,10 @@ class MXBeanIntrospector extends MBeanIntrospector<ConvertingMethod> {
final boolean isWritable = (setter != null); final boolean isWritable = (setter != null);
final boolean isIs = isReadable && getName(getter).startsWith("is"); 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 OpenType<?> openType;
final Type originalType; final Type originalType;
...@@ -229,13 +234,17 @@ class MXBeanIntrospector extends MBeanIntrospector<ConvertingMethod> { ...@@ -229,13 +234,17 @@ class MXBeanIntrospector extends MBeanIntrospector<ConvertingMethod> {
MBeanOperationInfo getMBeanOperationInfo(String operationName, MBeanOperationInfo getMBeanOperationInfo(String operationName,
ConvertingMethod operation) { ConvertingMethod operation) {
final Method method = operation.getMethod(); final Method method = operation.getMethod();
final String description = operationName; String description = operationName;
/* Ideally this would be an empty string, but /* Ideally this would be an empty string, but
OMBOperationInfo constructor forbids that. Also, we OMBOperationInfo constructor forbids that. */
could consult an annotation to get a useful Description d = method.getAnnotation(Description.class);
description. */ 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 OpenType<?> returnType = operation.getOpenReturnType();
final Type originalReturnType = operation.getGenericReturnType(); final Type originalReturnType = operation.getGenericReturnType();
...@@ -247,8 +256,15 @@ class MXBeanIntrospector extends MBeanIntrospector<ConvertingMethod> { ...@@ -247,8 +256,15 @@ class MXBeanIntrospector extends MBeanIntrospector<ConvertingMethod> {
boolean openParameterTypes = true; boolean openParameterTypes = true;
Annotation[][] annots = method.getParameterAnnotations(); Annotation[][] annots = method.getParameterAnnotations();
for (int i = 0; i < paramTypes.length; i++) { for (int i = 0; i < paramTypes.length; i++) {
final String paramName = "p" + i; String paramName = Introspector.nameForParameter(annots[i]);
final String paramDescription = paramName; if (paramName == null)
paramName = "p" + i;
String paramDescription =
Introspector.descriptionForParameter(annots[i]);
if (paramDescription == null)
paramDescription = paramName;
final OpenType<?> openType = paramTypes[i]; final OpenType<?> openType = paramTypes[i];
final Type originalType = originalParamTypes[i]; final Type originalType = originalParamTypes[i];
Descriptor descriptor = Descriptor descriptor =
......
...@@ -161,7 +161,7 @@ public class MXBeanSupport extends MBeanSupport<ConvertingMethod> { ...@@ -161,7 +161,7 @@ public class MXBeanSupport extends MBeanSupport<ConvertingMethod> {
synchronized (lock) { synchronized (lock) {
this.mxbeanLookup = MXBeanLookup.Plain.lookupFor(server); this.mxbeanLookup = MXBeanLookup.Plain.lookupFor(server);
this.mxbeanLookup.addReference(name, getResource()); this.mxbeanLookup.addReference(name, getWrappedObject());
this.objectName = name; this.objectName = name;
} }
} }
...@@ -170,7 +170,7 @@ public class MXBeanSupport extends MBeanSupport<ConvertingMethod> { ...@@ -170,7 +170,7 @@ public class MXBeanSupport extends MBeanSupport<ConvertingMethod> {
public void unregister() { public void unregister() {
synchronized (lock) { synchronized (lock) {
if (mxbeanLookup != null) { if (mxbeanLookup != null) {
if (mxbeanLookup.removeReference(objectName, getResource())) if (mxbeanLookup.removeReference(objectName, getWrappedObject()))
objectName = null; objectName = null;
} }
// XXX: need to revisit the whole register/unregister logic in // XXX: need to revisit the whole register/unregister logic in
......
/*
* 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;
}
}
...@@ -29,6 +29,7 @@ import com.sun.jmx.defaults.ServiceName; ...@@ -29,6 +29,7 @@ import com.sun.jmx.defaults.ServiceName;
import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
...@@ -39,7 +40,6 @@ import java.util.Set; ...@@ -39,7 +40,6 @@ import java.util.Set;
import javax.management.DynamicMBean; import javax.management.DynamicMBean;
import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException; import javax.management.InstanceNotFoundException;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName; import javax.management.ObjectName;
import javax.management.QueryExp; import javax.management.QueryExp;
import javax.management.RuntimeOperationsException; import javax.management.RuntimeOperationsException;
...@@ -52,6 +52,27 @@ import javax.management.RuntimeOperationsException; ...@@ -52,6 +52,27 @@ import javax.management.RuntimeOperationsException;
*/ */
public class Repository { 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 --------------------------------------------> // Private fields -------------------------------------------->
/** /**
...@@ -115,7 +136,6 @@ public class Repository { ...@@ -115,7 +136,6 @@ public class Repository {
/** /**
* Builds a new ObjectNamePattern object from an ObjectName pattern * Builds a new ObjectNamePattern object from an ObjectName pattern
* constituents. * constituents.
* @param domain pattern.getDomain().
* @param propertyListPattern pattern.isPropertyListPattern(). * @param propertyListPattern pattern.isPropertyListPattern().
* @param propertyValuePattern pattern.isPropertyValuePattern(). * @param propertyValuePattern pattern.isPropertyValuePattern().
* @param canonicalProps pattern.getCanonicalKeyPropertyListString(). * @param canonicalProps pattern.getCanonicalKeyPropertyListString().
...@@ -216,16 +236,6 @@ public class Repository { ...@@ -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 /** Match a string against a shell-style pattern. The only pattern
characters recognised are <code>?</code>, standing for any one characters recognised are <code>?</code>, standing for any one
character, and <code>*</code>, standing for any string of character, and <code>*</code>, standing for any string of
...@@ -306,6 +316,50 @@ public class Repository { ...@@ -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 * Retrieves the named object contained in repository
* from the given objectname. * from the given objectname.
...@@ -355,12 +409,12 @@ public class Repository { ...@@ -355,12 +409,12 @@ public class Repository {
domainTb = new HashMap<String,Map<String,NamedObject>>(5); domainTb = new HashMap<String,Map<String,NamedObject>>(5);
if (domain != null && domain.length() != 0) if (domain != null && domain.length() != 0)
this.domain = domain; this.domain = domain.intern(); // we use == domain later on...
else else
this.domain = ServiceName.DOMAIN; this.domain = ServiceName.DOMAIN;
// Creates an new hastable for the default domain // Creates a new hashtable for the default domain
domainTb.put(this.domain.intern(), new HashMap<String,NamedObject>()); domainTb.put(this.domain, new HashMap<String,NamedObject>());
} }
/** /**
...@@ -395,10 +449,21 @@ public class Repository { ...@@ -395,10 +449,21 @@ public class Repository {
/** /**
* Stores an MBean associated with its object name in the repository. * Stores an MBean associated with its object name in the repository.
* *
* @param object MBean to be stored in the repository. * @param object MBean to be stored in the repository.
* @param name MBean object name. * @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 { throws InstanceAlreadyExistsException {
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
...@@ -431,7 +496,7 @@ public class Repository { ...@@ -431,7 +496,7 @@ public class Repository {
lock.writeLock().lock(); lock.writeLock().lock();
try { try {
// Domain cannot be JMImplementation if entry does not exists // Domain cannot be JMImplementation if entry does not exist
if ( !to_default_domain && if ( !to_default_domain &&
dom.equals("JMImplementation") && dom.equals("JMImplementation") &&
domainTb.containsKey("JMImplementation")) { domainTb.containsKey("JMImplementation")) {
...@@ -440,21 +505,21 @@ public class Repository { ...@@ -440,21 +505,21 @@ public class Repository {
"Repository: domain name cannot be JMImplementation")); "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); final Map<String,NamedObject> moiTb = domainTb.get(dom);
if (moiTb == null) { if (moiTb == null) {
addNewDomMoi(object, dom, name); addNewDomMoi(object, dom, name, context);
return; 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 { } else {
nbElements++; // Add instance if not already present
moiTb.put(cstr, new NamedObject(name, object)); 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 { } finally {
...@@ -533,7 +598,7 @@ public class Repository { ...@@ -533,7 +598,7 @@ public class Repository {
// ":*", ":[key=value],*" : names in defaultDomain // ":*", ":[key=value],*" : names in defaultDomain
// "domain:*", "domain:[key=value],*" : names in the specified domain // "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; ObjectName name;
if (pattern == null || if (pattern == null ||
pattern.getCanonicalName().length() == 0 || pattern.getCanonicalName().length() == 0 ||
...@@ -546,8 +611,7 @@ public class Repository { ...@@ -546,8 +611,7 @@ public class Repository {
// If pattern is not a pattern, retrieve this mbean ! // If pattern is not a pattern, retrieve this mbean !
if (!name.isPattern()) { if (!name.isPattern()) {
final NamedObject no; final NamedObject no = retrieveNamedObject(name);
no = retrieveNamedObject(name);
if (no != null) result.add(no); if (no != null) result.add(no);
return result; return result;
} }
...@@ -577,12 +641,22 @@ public class Repository { ...@@ -577,12 +641,22 @@ public class Repository {
return result; 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 (*, ?) // Pattern matching in the domain name (*, ?)
char[] dom2Match = name.getDomain().toCharArray(); char[] dom2Match = name.getDomain().toCharArray();
for (String domain : domainTb.keySet()) { for (String dom : domainTb.keySet()) {
char[] theDom = domain.toCharArray(); char[] theDom = dom.toCharArray();
if (wildmatch(theDom, dom2Match)) { if (wildmatch(theDom, dom2Match)) {
final Map<String,NamedObject> moiTb = domainTb.get(domain); final Map<String,NamedObject> moiTb = domainTb.get(dom);
if (allNames) if (allNames)
result.addAll(moiTb.values()); result.addAll(moiTb.values());
else else
...@@ -599,11 +673,21 @@ public class Repository { ...@@ -599,11 +673,21 @@ public class Repository {
* Removes an MBean from the repository. * Removes an MBean from the repository.
* *
* @param name name of the MBean to remove. * @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 * @exception InstanceNotFoundException The MBean does not exist in
* the repository. * the repository.
*/ */
public void remove(final ObjectName name) public void remove(final ObjectName name,
final RegistrationContext context)
throws InstanceNotFoundException { throws InstanceNotFoundException {
// Debugging stuff // Debugging stuff
...@@ -645,6 +729,9 @@ public class Repository { ...@@ -645,6 +729,9 @@ public class Repository {
if (dom == domain) if (dom == domain)
domainTb.put(domain, new HashMap<String,NamedObject>()); domainTb.put(domain, new HashMap<String,NamedObject>());
} }
unregistering(context,name);
} finally { } finally {
lock.writeLock().unlock(); lock.writeLock().unlock();
} }
......
...@@ -35,6 +35,7 @@ import javax.management.IntrospectionException; ...@@ -35,6 +35,7 @@ import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo; import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException; import javax.management.MBeanException;
import javax.management.MBeanOperationInfo; import javax.management.MBeanOperationInfo;
import javax.management.ManagedOperation;
import javax.management.NotCompliantMBeanException; import javax.management.NotCompliantMBeanException;
import javax.management.NotificationBroadcaster; import javax.management.NotificationBroadcaster;
import javax.management.NotificationBroadcasterSupport; import javax.management.NotificationBroadcasterSupport;
...@@ -118,22 +119,32 @@ class StandardMBeanIntrospector extends MBeanIntrospector<Method> { ...@@ -118,22 +119,32 @@ class StandardMBeanIntrospector extends MBeanIntrospector<Method> {
@Override @Override
MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, MBeanAttributeInfo getMBeanAttributeInfo(String attributeName,
Method getter, Method setter) { Method getter, Method setter) throws IntrospectionException {
final String description = "Attribute exposed for management"; String description = getAttributeDescription(
try { attributeName, "Attribute exposed for management",
return new MBeanAttributeInfo(attributeName, description, getter, setter);
getter, setter); return new MBeanAttributeInfo(attributeName, description,
} catch (IntrospectionException e) { getter, setter);
throw new RuntimeException(e); // should not happen
}
} }
@Override @Override
MBeanOperationInfo getMBeanOperationInfo(String operationName, MBeanOperationInfo getMBeanOperationInfo(String operationName,
Method operation) { Method operation) {
final String description = "Operation exposed for management"; final String defaultDescription = "Operation exposed for management";
return new MBeanOperationInfo(description, operation); 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 @Override
......
...@@ -41,26 +41,24 @@ import javax.management.openmbean.MXBeanMappingFactory; ...@@ -41,26 +41,24 @@ import javax.management.openmbean.MXBeanMappingFactory;
public class StandardMBeanSupport extends MBeanSupport<Method> { public class StandardMBeanSupport extends MBeanSupport<Method> {
/** /**
<p>Construct a Standard MBean that wraps the given resource using the * <p>Construct a Standard MBean that wraps the given resource using the
given Standard MBean interface.</p> * given Standard MBean interface.</p>
*
@param resource the underlying resource for the new MBean. * @param resource the underlying resource for the new MBean.
* @param mbeanInterfaceType the class or interface to be used to determine
@param mbeanInterface the interface to be used to determine * the MBean's management interface. An interface if this is a
the MBean's management interface. * classic Standard MBean; a class if this is a {@code @ManagedResource}.
* @param <T> a type parameter that allows the compiler to check
@param <T> a type parameter that allows the compiler to check * that {@code resource} implements {@code mbeanInterfaceType},
that {@code resource} implements {@code mbeanInterface}, * provided that {@code mbeanInterfaceType} is a class constant like
provided that {@code mbeanInterface} is a class constant like * {@code SomeMBean.class}.
{@code SomeMBean.class}. * @throws IllegalArgumentException if {@code resource} is null or
* if it does not implement the class {@code mbeanInterfaceType} or if
@throws IllegalArgumentException if {@code resource} is null or * that class is not a valid Standard MBean interface.
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> mbeanInterfaceType)
*/
public <T> StandardMBeanSupport(T resource, Class<T> mbeanInterface)
throws NotCompliantMBeanException { throws NotCompliantMBeanException {
super(resource, mbeanInterface, (MXBeanMappingFactory) null); super(resource, mbeanInterfaceType, (MXBeanMappingFactory) null);
} }
@Override @Override
...@@ -86,13 +84,14 @@ public class StandardMBeanSupport extends MBeanSupport<Method> { ...@@ -86,13 +84,14 @@ public class StandardMBeanSupport extends MBeanSupport<Method> {
@Override @Override
public MBeanInfo getMBeanInfo() { public MBeanInfo getMBeanInfo() {
MBeanInfo mbi = super.getMBeanInfo(); MBeanInfo mbi = super.getMBeanInfo();
Class<?> resourceClass = getResource().getClass(); Class<?> resourceClass = getWrappedObject().getClass();
if (StandardMBeanIntrospector.isDefinitelyImmutableInfo(resourceClass)) if (!getMBeanInterface().isInterface() ||
StandardMBeanIntrospector.isDefinitelyImmutableInfo(resourceClass))
return mbi; return mbi;
return new MBeanInfo(mbi.getClassName(), mbi.getDescription(), return new MBeanInfo(mbi.getClassName(), mbi.getDescription(),
mbi.getAttributes(), mbi.getConstructors(), mbi.getAttributes(), mbi.getConstructors(),
mbi.getOperations(), mbi.getOperations(),
MBeanIntrospector.findNotifications(getResource()), MBeanIntrospector.findNotifications(getWrappedObject()),
mbi.getDescriptor()); mbi.getDescriptor());
} }
} }
...@@ -38,6 +38,7 @@ import java.util.Map; ...@@ -38,6 +38,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.WeakHashMap;
import javax.management.MalformedObjectNameException; import javax.management.MalformedObjectNameException;
import javax.management.ObjectName; import javax.management.ObjectName;
...@@ -71,6 +72,10 @@ public class Util { ...@@ -71,6 +72,10 @@ public class Util {
return new LinkedHashMap<K, V>(); return new LinkedHashMap<K, V>();
} }
static <K, V> WeakHashMap<K, V> newWeakHashMap() {
return new WeakHashMap<K, V>();
}
static <E> Set<E> newSet() { static <E> Set<E> newSet() {
return new HashSet<E>(); return new HashSet<E>();
} }
......
...@@ -208,8 +208,9 @@ public class EventSetImpl extends ArrayList<Event> implements EventSet { ...@@ -208,8 +208,9 @@ public class EventSetImpl extends ArrayList<Event> implements EventSet {
} }
public String toString() { public String toString() {
return eventName() + "@" + location().toString() + return eventName() + "@" +
" in thread " + thread().name(); ((location() == null) ? " null" : location().toString()) +
" in thread " + thread().name();
} }
} }
......
...@@ -40,11 +40,12 @@ public class MonitorInfoImpl extends MirrorImpl ...@@ -40,11 +40,12 @@ public class MonitorInfoImpl extends MirrorImpl
int stack_depth; int stack_depth;
MonitorInfoImpl(VirtualMachine vm, ObjectReference mon, MonitorInfoImpl(VirtualMachine vm, ObjectReference mon,
ThreadReference thread, int dpth) { ThreadReferenceImpl thread, int dpth) {
super(vm); super(vm);
this.monitor = mon; this.monitor = mon;
this.thread = thread; this.thread = thread;
this.stack_depth = dpth; this.stack_depth = dpth;
thread.addListener(this);
} }
......
...@@ -35,12 +35,34 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -35,12 +35,34 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
static final int SUSPEND_STATUS_SUSPENDED = 0x1; static final int SUSPEND_STATUS_SUSPENDED = 0x1;
static final int SUSPEND_STATUS_BREAK = 0x2; static final int SUSPEND_STATUS_BREAK = 0x2;
private ThreadGroupReference threadGroup;
private int suspendedZombieCount = 0; private int suspendedZombieCount = 0;
// This is cached only while the VM is suspended /*
private static class Cache extends ObjectReferenceImpl.Cache { * Some objects can only be created while a thread is suspended and are valid
String name = null; * only while the thread remains suspended. Examples are StackFrameImpl
* and MonitorInfoImpl. When the thread resumes, these objects have to be
* marked as invalid so that their methods can throw
* InvalidStackFrameException if they are called. To do this, such objects
* register themselves as listeners of the associated thread. When the
* thread is resumed, its listeners are notified and mark themselves
* invalid.
* Also, note that ThreadReferenceImpl itself caches some info that
* is valid only as long as the thread is suspended. When the thread
* is resumed, that cache must be purged.
* Lastly, note that ThreadReferenceImpl and its super, ObjectReferenceImpl
* cache some info that is only valid as long as the entire VM is suspended.
* If _any_ thread is resumed, this cache must be purged. To handle this,
* both ThreadReferenceImpl and ObjectReferenceImpl register themselves as
* VMListeners so that they get notified when all threads are suspended and
* when any thread is resumed.
*/
// This is cached for the life of the thread
private ThreadGroupReference threadGroup;
// This is cached only while this one thread is suspended. Each time
// the thread is resumed, we clear this and start with a fresh one.
private static class LocalCache {
JDWP.ThreadReference.Status status = null; JDWP.ThreadReference.Status status = null;
List<StackFrame> frames = null; List<StackFrame> frames = null;
int framesStart = -1; int framesStart = -1;
...@@ -52,6 +74,17 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -52,6 +74,17 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
boolean triedCurrentContended = false; boolean triedCurrentContended = false;
} }
private LocalCache localCache;
private void resetLocalCache() {
localCache = new LocalCache();
}
// This is cached only while all threads in the VM are suspended
// Yes, someone could change the name of a thread while it is suspended.
private static class Cache extends ObjectReferenceImpl.Cache {
String name = null;
}
protected ObjectReferenceImpl.Cache newCache() { protected ObjectReferenceImpl.Cache newCache() {
return new Cache(); return new Cache();
} }
...@@ -59,8 +92,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -59,8 +92,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
// Listeners - synchronized on vm.state() // Listeners - synchronized on vm.state()
private List<WeakReference<ThreadListener>> listeners = new ArrayList<WeakReference<ThreadListener>>(); private List<WeakReference<ThreadListener>> listeners = new ArrayList<WeakReference<ThreadListener>>();
ThreadReferenceImpl(VirtualMachine aVm, long aRef) { ThreadReferenceImpl(VirtualMachine aVm, long aRef) {
super(aVm,aRef); super(aVm,aRef);
resetLocalCache();
vm.state().addListener(this); vm.state().addListener(this);
} }
...@@ -72,10 +107,24 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -72,10 +107,24 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
* VMListener implementation * VMListener implementation
*/ */
public boolean vmNotSuspended(VMAction action) { public boolean vmNotSuspended(VMAction action) {
synchronized (vm.state()) { if (action.resumingThread() == null) {
processThreadAction(new ThreadAction(this, // all threads are being resumed
ThreadAction.THREAD_RESUMABLE)); synchronized (vm.state()) {
processThreadAction(new ThreadAction(this,
ThreadAction.THREAD_RESUMABLE));
}
} }
/*
* Othewise, only one thread is being resumed:
* if it is us,
* we have already done our processThreadAction to notify our
* listeners when we processed the resume.
* if it is not us,
* we don't want to notify our listeners
* because we are not being resumed.
*/
return super.vmNotSuspended(action); return super.vmNotSuspended(action);
} }
...@@ -191,23 +240,19 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -191,23 +240,19 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
} }
private JDWP.ThreadReference.Status jdwpStatus() { private JDWP.ThreadReference.Status jdwpStatus() {
JDWP.ThreadReference.Status status = null; JDWP.ThreadReference.Status myStatus = localCache.status;
try { try {
Cache local = (Cache)getCache(); if (myStatus == null) {
myStatus = JDWP.ThreadReference.Status.process(vm, this);
if (local != null) { if ((myStatus.suspendStatus & SUSPEND_STATUS_SUSPENDED) != 0) {
status = local.status; // thread is suspended, we can cache the status.
} localCache.status = myStatus;
if (status == null) {
status = JDWP.ThreadReference.Status.process(vm, this);
if (local != null) {
local.status = status;
} }
} }
} catch (JDWPException exc) { } catch (JDWPException exc) {
throw exc.toJDIException(); throw exc.toJDIException();
} }
return status; return myStatus;
} }
public int status() { public int status() {
...@@ -245,8 +290,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -245,8 +290,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
public ThreadGroupReference threadGroup() { public ThreadGroupReference threadGroup() {
/* /*
* Thread group can't change, so it's cached more conventionally * Thread group can't change, so it's cached once and for all.
* than other things in this class.
*/ */
if (threadGroup == null) { if (threadGroup == null) {
try { try {
...@@ -260,19 +304,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -260,19 +304,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
} }
public int frameCount() throws IncompatibleThreadStateException { public int frameCount() throws IncompatibleThreadStateException {
int frameCount = -1;
try { try {
Cache local = (Cache)getCache(); if (localCache.frameCount == -1) {
localCache.frameCount = JDWP.ThreadReference.FrameCount
if (local != null) {
frameCount = local.frameCount;
}
if (frameCount == -1) {
frameCount = JDWP.ThreadReference.FrameCount
.process(vm, this).frameCount; .process(vm, this).frameCount;
if (local != null) {
local.frameCount = frameCount;
}
} }
} catch (JDWPException exc) { } catch (JDWPException exc) {
switch (exc.errorCode()) { switch (exc.errorCode()) {
...@@ -283,7 +318,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -283,7 +318,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
throw exc.toJDIException(); throw exc.toJDIException();
} }
} }
return frameCount; return localCache.frameCount;
} }
public List<StackFrame> frames() throws IncompatibleThreadStateException { public List<StackFrame> frames() throws IncompatibleThreadStateException {
...@@ -297,23 +332,25 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -297,23 +332,25 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
/** /**
* Is the requested subrange within what has been retrieved? * Is the requested subrange within what has been retrieved?
* local is known to be non-null * local is known to be non-null. Should only be called from
* a sync method.
*/ */
private boolean isSubrange(Cache local, private boolean isSubrange(LocalCache localCache,
int start, int length, List frames) { int start, int length) {
if (start < local.framesStart) { if (start < localCache.framesStart) {
return false; return false;
} }
if (length == -1) { if (length == -1) {
return (local.framesLength == -1); return (localCache.framesLength == -1);
} }
if (local.framesLength == -1) { if (localCache.framesLength == -1) {
if ((start + length) > (local.framesStart + frames.size())) { if ((start + length) > (localCache.framesStart +
localCache.frames.size())) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
return true; return true;
} }
return ((start + length) <= (local.framesStart + local.framesLength)); return ((start + length) <= (localCache.framesStart + localCache.framesLength));
} }
public List<StackFrame> frames(int start, int length) public List<StackFrame> frames(int start, int length)
...@@ -329,51 +366,42 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -329,51 +366,42 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
* Private version of frames() allows "-1" to specify all * Private version of frames() allows "-1" to specify all
* remaining frames. * remaining frames.
*/ */
private List<StackFrame> privateFrames(int start, int length) synchronized private List<StackFrame> privateFrames(int start, int length)
throws IncompatibleThreadStateException { throws IncompatibleThreadStateException {
List<StackFrame> frames = null;
try {
Cache local = (Cache)getCache();
if (local != null) { // Lock must be held while creating stack frames so if that two threads
frames = local.frames; // do this at the same time, one won't clobber the subset created by the other.
}
if (frames == null || !isSubrange(local, start, length, frames)) { try {
if (localCache.frames == null || !isSubrange(localCache, start, length)) {
JDWP.ThreadReference.Frames.Frame[] jdwpFrames JDWP.ThreadReference.Frames.Frame[] jdwpFrames
= JDWP.ThreadReference.Frames. = JDWP.ThreadReference.Frames.
process(vm, this, start, length).frames; process(vm, this, start, length).frames;
int count = jdwpFrames.length; int count = jdwpFrames.length;
frames = new ArrayList<StackFrame>(count); localCache.frames = new ArrayList<StackFrame>(count);
// Lock must be held while creating stack frames. for (int i = 0; i<count; i++) {
// so that a resume will not resume a partially if (jdwpFrames[i].location == null) {
// created stack. throw new InternalException("Invalid frame location");
synchronized (vm.state()) {
for (int i = 0; i<count; i++) {
if (jdwpFrames[i].location == null) {
throw new InternalException("Invalid frame location");
}
StackFrame frame = new StackFrameImpl(vm, this,
jdwpFrames[i].frameID,
jdwpFrames[i].location);
// Add to the frame list
frames.add(frame);
} }
StackFrame frame = new StackFrameImpl(vm, this,
jdwpFrames[i].frameID,
jdwpFrames[i].location);
// Add to the frame list
localCache.frames.add(frame);
} }
if (local != null) { localCache.framesStart = start;
local.frames = frames; localCache.framesLength = length;
local.framesStart = start; return Collections.unmodifiableList(localCache.frames);
local.framesLength = length;
}
} else { } else {
int fromIndex = start - local.framesStart; int fromIndex = start - localCache.framesStart;
int toIndex; int toIndex;
if (length == -1) { if (length == -1) {
toIndex = frames.size() - fromIndex; toIndex = localCache.frames.size() - fromIndex;
} else { } else {
toIndex = fromIndex + length; toIndex = fromIndex + length;
} }
frames = frames.subList(fromIndex, toIndex); return Collections.unmodifiableList(localCache.frames.subList(fromIndex, toIndex));
} }
} catch (JDWPException exc) { } catch (JDWPException exc) {
switch (exc.errorCode()) { switch (exc.errorCode()) {
...@@ -384,28 +412,18 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -384,28 +412,18 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
throw exc.toJDIException(); throw exc.toJDIException();
} }
} }
return Collections.unmodifiableList(frames);
} }
public List<ObjectReference> ownedMonitors() throws IncompatibleThreadStateException { public List<ObjectReference> ownedMonitors() throws IncompatibleThreadStateException {
List<ObjectReference> monitors = null;
try { try {
Cache local = (Cache)getCache(); if (localCache.ownedMonitors == null) {
localCache.ownedMonitors = Arrays.asList(
if (local != null) {
monitors = local.ownedMonitors;
}
if (monitors == null) {
monitors = Arrays.asList(
(ObjectReference[])JDWP.ThreadReference.OwnedMonitors. (ObjectReference[])JDWP.ThreadReference.OwnedMonitors.
process(vm, this).owned); process(vm, this).owned);
if (local != null) { if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
local.ownedMonitors = monitors; vm.printTrace(description() +
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { " temporarily caching owned monitors"+
vm.printTrace(description() + " (count = " + localCache.ownedMonitors.size() + ")");
" temporarily caching owned monitors"+
" (count = " + monitors.size() + ")");
}
} }
} }
} catch (JDWPException exc) { } catch (JDWPException exc) {
...@@ -417,29 +435,22 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -417,29 +435,22 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
throw exc.toJDIException(); throw exc.toJDIException();
} }
} }
return monitors; return localCache.ownedMonitors;
} }
public ObjectReference currentContendedMonitor() public ObjectReference currentContendedMonitor()
throws IncompatibleThreadStateException { throws IncompatibleThreadStateException {
ObjectReference monitor = null;
try { try {
Cache local = (Cache)getCache(); if (localCache.contendedMonitor == null &&
!localCache.triedCurrentContended) {
if (local != null && local.triedCurrentContended) { localCache.contendedMonitor = JDWP.ThreadReference.CurrentContendedMonitor.
monitor = local.contendedMonitor;
} else {
monitor = JDWP.ThreadReference.CurrentContendedMonitor.
process(vm, this).monitor; process(vm, this).monitor;
if (local != null) { localCache.triedCurrentContended = true;
local.triedCurrentContended = true; if ((localCache.contendedMonitor != null) &&
local.contendedMonitor = monitor; ((vm.traceFlags & vm.TRACE_OBJREFS) != 0)) {
if ((monitor != null) && vm.printTrace(description() +
((vm.traceFlags & vm.TRACE_OBJREFS) != 0)) { " temporarily caching contended monitor"+
vm.printTrace(description() + " (id = " + localCache.contendedMonitor.uniqueID() + ")");
" temporarily caching contended monitor"+
" (id = " + monitor.uniqueID() + ")");
}
} }
} }
} catch (JDWPException exc) { } catch (JDWPException exc) {
...@@ -450,40 +461,31 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -450,40 +461,31 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
throw exc.toJDIException(); throw exc.toJDIException();
} }
} }
return monitor; return localCache.contendedMonitor;
} }
public List<MonitorInfo> ownedMonitorsAndFrames() throws IncompatibleThreadStateException { public List<MonitorInfo> ownedMonitorsAndFrames() throws IncompatibleThreadStateException {
List<MonitorInfo> monitors = null;
try { try {
Cache local = (Cache)getCache(); if (localCache.ownedMonitorsInfo == null) {
if (local != null) {
monitors = local.ownedMonitorsInfo;
}
if (monitors == null) {
JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor[] minfo; JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor[] minfo;
minfo = JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.process(vm, this).owned; minfo = JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.process(vm, this).owned;
monitors = new ArrayList<MonitorInfo>(minfo.length); localCache.ownedMonitorsInfo = new ArrayList<MonitorInfo>(minfo.length);
for (int i=0; i < minfo.length; i++) { for (int i=0; i < minfo.length; i++) {
JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor mi = JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor mi =
minfo[i]; minfo[i];
MonitorInfo mon = new MonitorInfoImpl(vm, minfo[i].monitor, this, minfo[i].stack_depth); MonitorInfo mon = new MonitorInfoImpl(vm, minfo[i].monitor, this, minfo[i].stack_depth);
monitors.add(mon); localCache.ownedMonitorsInfo.add(mon);
} }
if (local != null) { if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
local.ownedMonitorsInfo = monitors; vm.printTrace(description() +
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { " temporarily caching owned monitors"+
vm.printTrace(description() + " (count = " + localCache.ownedMonitorsInfo.size() + ")");
" temporarily caching owned monitors"+
" (count = " + monitors.size() + ")");
} }
} }
}
} catch (JDWPException exc) { } catch (JDWPException exc) {
switch (exc.errorCode()) { switch (exc.errorCode()) {
case JDWP.Error.THREAD_NOT_SUSPENDED: case JDWP.Error.THREAD_NOT_SUSPENDED:
...@@ -493,7 +495,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -493,7 +495,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
throw exc.toJDIException(); throw exc.toJDIException();
} }
} }
return monitors; return localCache.ownedMonitorsInfo;
} }
public void popFrames(StackFrame frame) throws IncompatibleThreadStateException { public void popFrames(StackFrame frame) throws IncompatibleThreadStateException {
...@@ -511,7 +513,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -511,7 +513,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
} }
public void forceEarlyReturn(Value returnValue) throws InvalidTypeException, public void forceEarlyReturn(Value returnValue) throws InvalidTypeException,
ClassNotLoadedException, ClassNotLoadedException,
IncompatibleThreadStateException { IncompatibleThreadStateException {
if (!vm.canForceEarlyReturn()) { if (!vm.canForceEarlyReturn()) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
...@@ -604,6 +606,9 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl ...@@ -604,6 +606,9 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
iter.remove(); iter.remove();
} }
} }
// Discard our local cache
resetLocalCache();
} }
} }
} }
...@@ -38,10 +38,18 @@ class VMAction extends EventObject { ...@@ -38,10 +38,18 @@ class VMAction extends EventObject {
static final int VM_NOT_SUSPENDED = 2; static final int VM_NOT_SUSPENDED = 2;
int id; int id;
ThreadReference resumingThread;
VMAction(VirtualMachine vm, int id) { VMAction(VirtualMachine vm, int id) {
this(vm, null, id);
}
// For id = VM_NOT_SUSPENDED, if resumingThread != null, then it is
// the only thread that is being resumed.
VMAction(VirtualMachine vm, ThreadReference resumingThread, int id) {
super(vm); super(vm);
this.id = id; this.id = id;
this.resumingThread = resumingThread;
} }
VirtualMachine vm() { VirtualMachine vm() {
return (VirtualMachine)getSource(); return (VirtualMachine)getSource();
...@@ -49,4 +57,8 @@ class VMAction extends EventObject { ...@@ -49,4 +57,8 @@ class VMAction extends EventObject {
int id() { int id() {
return id; return id;
} }
ThreadReference resumingThread() {
return resumingThread;
}
} }
...@@ -115,17 +115,26 @@ class VMState { ...@@ -115,17 +115,26 @@ class VMState {
return stream; return stream;
} }
/**
* All threads are resuming
*/
void thaw() {
thaw(null);
}
/** /**
* Tell listeners to invalidate suspend-sensitive caches. * Tell listeners to invalidate suspend-sensitive caches.
* If resumingThread != null, then only that thread is being
* resumed.
*/ */
synchronized void thaw() { synchronized void thaw(ThreadReference resumingThread) {
if (cache != null) { if (cache != null) {
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
vm.printTrace("Clearing VM suspended cache"); vm.printTrace("Clearing VM suspended cache");
} }
disableCache(); disableCache();
} }
processVMAction(new VMAction(vm, VMAction.VM_NOT_SUSPENDED)); processVMAction(new VMAction(vm, resumingThread, VMAction.VM_NOT_SUSPENDED));
} }
private synchronized void processVMAction(VMAction action) { private synchronized void processVMAction(VMAction action) {
......
...@@ -146,8 +146,9 @@ class VirtualMachineImpl extends MirrorImpl ...@@ -146,8 +146,9 @@ class VirtualMachineImpl extends MirrorImpl
public boolean threadResumable(ThreadAction action) { public boolean threadResumable(ThreadAction action) {
/* /*
* If any thread is resumed, the VM is considered not suspended. * If any thread is resumed, the VM is considered not suspended.
* Just one thread is being resumed so pass it to thaw.
*/ */
state.thaw(); state.thaw(action.thread());
return true; return true;
} }
......
...@@ -191,7 +191,7 @@ public abstract class SelectionKey { ...@@ -191,7 +191,7 @@ public abstract class SelectionKey {
* @throws IllegalArgumentException * @throws IllegalArgumentException
* If a bit in the set does not correspond to an operation that * If a bit in the set does not correspond to an operation that
* is supported by this key's channel, that is, if * is supported by this key's channel, that is, if
* <tt>set & ~(channel().validOps()) != 0</tt> * <tt>(ops & ~channel().validOps()) != 0</tt>
* *
* @throws CancelledKeyException * @throws CancelledKeyException
* If this key has been cancelled * If this key has been cancelled
......
...@@ -192,6 +192,7 @@ class BinaryRelQueryExp extends QueryEval implements QueryExp { ...@@ -192,6 +192,7 @@ class BinaryRelQueryExp extends QueryEval implements QueryExp {
return "(" + exp1 + ") " + relOpString() + " (" + exp2 + ")"; return "(" + exp1 + ") " + relOpString() + " (" + exp2 + ")";
} }
@Override
String toQueryString() { String toQueryString() {
return exp1 + " " + relOpString() + " " + exp2; return exp1 + " " + relOpString() + " " + exp2;
} }
......
/*
* 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 "";
}
...@@ -38,6 +38,7 @@ import java.util.Arrays; ...@@ -38,6 +38,7 @@ import java.util.Arrays;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeData;
import javax.management.openmbean.MXBeanMappingFactory;
import javax.management.openmbean.OpenMBeanAttributeInfoSupport; import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
import javax.management.openmbean.OpenMBeanOperationInfoSupport; import javax.management.openmbean.OpenMBeanOperationInfoSupport;
import javax.management.openmbean.OpenMBeanParameterInfoSupport; import javax.management.openmbean.OpenMBeanParameterInfoSupport;
...@@ -117,21 +118,19 @@ import javax.management.openmbean.OpenType; ...@@ -117,21 +118,19 @@ import javax.management.openmbean.OpenType;
* deprecation, for example {@code "1.3 Replaced by the Capacity * deprecation, for example {@code "1.3 Replaced by the Capacity
* attribute"}.</td> * 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 * <td>The base name for the {@link ResourceBundle} in which the key given in
* the {@code descriptionResourceKey} field can be found, for example * the {@code descriptionResourceKey} field can be found, for example
* {@code "com.example.myapp.MBeanResources"}. The meaning of this * {@code "com.example.myapp.MBeanResources"}.</td>
* field is defined by this specification but the field is not set or
* used by the JMX API itself.</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 * <td>A resource key for the description of this element. In
* conjunction with the {@code descriptionResourceBundleBaseName}, * conjunction with the {@code descriptionResourceBundleBaseName},
* this can be used to find a localized version of the description. * this can be used to find a localized version of the description.</td>
* The meaning of this field is defined by this specification but the
* field is not set or used by the JMX API itself.</td>
* *
* <tr><td>enabled</td><td>String</td> * <tr><td>enabled</td><td>String</td>
* <td>MBeanAttributeInfo<br>MBeanNotificationInfo<br>MBeanOperationInfo</td> * <td>MBeanAttributeInfo<br>MBeanNotificationInfo<br>MBeanOperationInfo</td>
...@@ -216,6 +215,14 @@ import javax.management.openmbean.OpenType; ...@@ -216,6 +215,14 @@ import javax.management.openmbean.OpenType;
* StandardMBean} class will have this field in its MBeanInfo * StandardMBean} class will have this field in its MBeanInfo
* Descriptor.</td> * 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> * <tr><td><a name="openType"><i>openType</i></a><td>{@link OpenType}</td>
* <td>MBeanAttributeInfo<br>MBeanOperationInfo<br>MBeanParameterInfo</td> * <td>MBeanAttributeInfo<br>MBeanOperationInfo<br>MBeanParameterInfo</td>
* *
......
/*
* 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();
}
...@@ -33,6 +33,11 @@ import java.lang.annotation.*; ...@@ -33,6 +33,11 @@ import java.lang.annotation.*;
* an MBean, or for an attribute, operation, or constructor in an * an MBean, or for an attribute, operation, or constructor in an
* MBean, or for a parameter of an operation or constructor.</p> * 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> * <p>Consider this annotation for example:</p>
* *
* <pre> * <pre>
...@@ -57,7 +62,7 @@ import java.lang.annotation.*; ...@@ -57,7 +62,7 @@ import java.lang.annotation.*;
* <p>When a Standard MBean is made from the {@code CacheControlMBean}, * <p>When a Standard MBean is made from the {@code CacheControlMBean},
* the usual rules mean that it will have an attribute called * the usual rules mean that it will have an attribute called
* {@code CacheSize} of type {@code long}. The {@code @Units} * {@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 * {@link MBeanAttributeInfo} for this attribute will have a
* {@code Descriptor} that has a field called {@code units} with * {@code Descriptor} that has a field called {@code units} with
* corresponding value {@code bytes}.</p> * corresponding value {@code bytes}.</p>
...@@ -125,12 +130,13 @@ import java.lang.annotation.*; ...@@ -125,12 +130,13 @@ import java.lang.annotation.*;
* the method in the child interface are considered. * the method in the child interface are considered.
* *
* <p>The Descriptor fields contributed in this way by different * <p>The Descriptor fields contributed in this way by different
* annotations on the same program element must be consistent. That * annotations on the same program element must be consistent with
* is, two different annotations, or two members of the same * each other and with any fields contributed by a {@link
* annotation, must not define a different value for the same * DescriptorFields &#64;DescriptorFields} annotation. That is, two
* Descriptor field. Fields from annotations on a getter method must * different annotations, or two members of the same annotation, must
* also be consistent with fields from annotations on the * not define a different value for the same Descriptor field. Fields
* corresponding setter method.</p> * 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 * <p>The Descriptor resulting from these annotations will be merged
* with any Descriptor fields provided by the implementation, such as * with any Descriptor fields provided by the implementation, such as
...@@ -169,4 +175,36 @@ import java.lang.annotation.*; ...@@ -169,4 +175,36 @@ import java.lang.annotation.*;
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
public @interface DescriptorKey { public @interface DescriptorKey {
String value(); 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;
} }
/*
* 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();
}
/*
* 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;
}
}
}
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
package javax.management; package javax.management;
import com.sun.jmx.mbeanserver.Introspector; import com.sun.jmx.mbeanserver.Introspector;
import com.sun.jmx.mbeanserver.MBeanInjector;
import com.sun.jmx.remote.util.ClassLogger; import com.sun.jmx.remote.util.ClassLogger;
import java.beans.BeanInfo; import java.beans.BeanInfo;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
...@@ -130,6 +131,7 @@ public class JMX { ...@@ -130,6 +131,7 @@ public class JMX {
* </pre> * </pre>
* *
* @see javax.management.JMX.ProxyOptions * @see javax.management.JMX.ProxyOptions
* @see javax.management.StandardMBean.Options
*/ */
public static class MBeanOptions implements Serializable, Cloneable { public static class MBeanOptions implements Serializable, Cloneable {
private static final long serialVersionUID = -6380842449318177843L; private static final long serialVersionUID = -6380842449318177843L;
...@@ -739,4 +741,28 @@ public class JMX { ...@@ -739,4 +741,28 @@ public class JMX {
// exactly the string "MXBean" since that would mean there // exactly the string "MXBean" since that would mean there
// was no package name, which is pretty unlikely in practice. // 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));
}
} }
/*
* 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 {
}
...@@ -46,25 +46,30 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable { ...@@ -46,25 +46,30 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable {
new MBeanOperationInfo[0]; new MBeanOperationInfo[0];
/** /**
* Indicates that the operation is read-like, * Indicates that the operation is read-like:
* it basically returns information. * it returns information but does not change any state.
* @see Impact#INFO
*/ */
public static final int INFO = 0; public static final int INFO = 0;
/** /**
* Indicates that the operation is a write-like, * Indicates that the operation is write-like: it has an effect but does
* and would modify the MBean in some way, typically by writing some value * not return any information from the MBean.
* or changing a configuration. * @see Impact#ACTION
*/ */
public static final int ACTION = 1; 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; 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; public static final int UNKNOWN = 3;
...@@ -120,8 +125,9 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable { ...@@ -120,8 +125,9 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable {
* describing the parameters(arguments) of the method. This may be * describing the parameters(arguments) of the method. This may be
* null with the same effect as a zero-length array. * null with the same effect as a zero-length array.
* @param type The type of the method's return value. * @param type The type of the method's return value.
* @param impact The impact of the method, one of <CODE>INFO, * @param impact The impact of the method, one of
* ACTION, ACTION_INFO, UNKNOWN</CODE>. * {@link #INFO}, {@link #ACTION}, {@link #ACTION_INFO},
* {@link #UNKNOWN}.
*/ */
public MBeanOperationInfo(String name, public MBeanOperationInfo(String name,
String description, String description,
...@@ -140,8 +146,9 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable { ...@@ -140,8 +146,9 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable {
* describing the parameters(arguments) of the method. This may be * describing the parameters(arguments) of the method. This may be
* null with the same effect as a zero-length array. * null with the same effect as a zero-length array.
* @param type The type of the method's return value. * @param type The type of the method's return value.
* @param impact The impact of the method, one of <CODE>INFO, * @param impact The impact of the method, one of
* ACTION, ACTION_INFO, UNKNOWN</CODE>. * {@link #INFO}, {@link #ACTION}, {@link #ACTION_INFO},
* {@link #UNKNOWN}.
* @param descriptor The descriptor for the operation. This may be null * @param descriptor The descriptor for the operation. This may be null
* which is equivalent to an empty descriptor. * which is equivalent to an empty descriptor.
* *
...@@ -319,9 +326,14 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable { ...@@ -319,9 +326,14 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable {
for (int i = 0; i < classes.length; i++) { for (int i = 0; i < classes.length; i++) {
Descriptor d = Introspector.descriptorForAnnotations(annots[i]); Descriptor d = Introspector.descriptorForAnnotations(annots[i]);
final String pn = "p" + (i + 1); String description = Introspector.descriptionForParameter(annots[i]);
params[i] = if (description == null)
new MBeanParameterInfo(pn, classes[i].getName(), "", d); 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; return params;
......
...@@ -27,9 +27,101 @@ package javax.management; ...@@ -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 * 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 * @since 1.5
*/ */
...@@ -38,12 +130,12 @@ public interface MBeanRegistration { ...@@ -38,12 +130,12 @@ public interface MBeanRegistration {
/** /**
* Allows the MBean to perform any operations it needs before * 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 * is not specified, the MBean can provide a name for its
* registration. If any exception is raised, the MBean will not be * 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 * @param name The object name of the MBean. This name is null if
* the name parameter to one of the <code>createMBean</code> or * the name parameter to one of the <code>createMBean</code> or
...@@ -57,7 +149,7 @@ public interface MBeanRegistration { ...@@ -57,7 +149,7 @@ public interface MBeanRegistration {
* the returned value. * the returned value.
* *
* @exception java.lang.Exception This exception will be caught by * @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}. * MBeanRegistrationException}.
*/ */
public ObjectName preRegister(MBeanServer server, public ObjectName preRegister(MBeanServer server,
......
...@@ -61,7 +61,7 @@ import javax.management.loading.ClassLoaderRepository; ...@@ -61,7 +61,7 @@ import javax.management.loading.ClassLoaderRepository;
* <CODE>ObjectName</CODE> is: <BR> * <CODE>ObjectName</CODE> is: <BR>
* <CODE>JMImplementation:type=MBeanServerDelegate</CODE>.</p> * <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 * MBeanServerFactory#createMBeanServer(String) createMBeanServer} or
* {@link MBeanServerFactory#newMBeanServer(String) newMBeanServer} * {@link MBeanServerFactory#newMBeanServer(String) newMBeanServer}
* methods of the {@link MBeanServerFactory} class applies security * methods of the {@link MBeanServerFactory} class applies security
...@@ -661,13 +661,16 @@ public interface MBeanServer extends MBeanServerConnection { ...@@ -661,13 +661,16 @@ public interface MBeanServer extends MBeanServerConnection {
ReflectionException; ReflectionException;
/** /**
* <p>Return the {@link java.lang.ClassLoader} that was used for * <p>Return the {@link java.lang.ClassLoader} that was used for loading
* loading the class of the named MBean.</p> * 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. * @param mbeanName The ObjectName of the MBean.
* *
* @return The ClassLoader used for that MBean. If <var>l</var> * @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: * returned value, then either:
* *
* <ul> * <ul>
......
...@@ -839,6 +839,12 @@ public interface MBeanServerConnection { ...@@ -839,6 +839,12 @@ public interface MBeanServerConnection {
* *
* <p>Otherwise, the result is false.</p> * <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 name The <CODE>ObjectName</CODE> of the MBean.
* @param className The name of the class. * @param className The name of the class.
* *
......
...@@ -27,6 +27,7 @@ package javax.management; ...@@ -27,6 +27,7 @@ package javax.management;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
...@@ -57,11 +58,13 @@ import javax.management.openmbean.TabularDataSupport; ...@@ -57,11 +58,13 @@ import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType; import javax.management.openmbean.TabularType;
/** /**
<p>Annotation to mark an interface explicitly as being an MXBean <p>Annotation to mark a class or interface explicitly as being an MXBean,
interface, or as not being an MXBean interface. By default, an or as not being an MXBean. By default, an
interface is an MXBean interface if its name ends with {@code interface is an MXBean interface if its name ends with {@code
MXBean}, as in {@code SomethingMXBean}. The following interfaces MXBean}, as in {@code SomethingMXBean}. A class is never an MXBean by
are MXBean interfaces:</p> default.</p>
<p>The following interfaces are MXBean interfaces:</p>
<pre> <pre>
public interface WhatsitMXBean {} public interface WhatsitMXBean {}
...@@ -82,6 +85,11 @@ import javax.management.openmbean.TabularType; ...@@ -82,6 +85,11 @@ import javax.management.openmbean.TabularType;
public interface MisleadingMXBean {} public interface MisleadingMXBean {}
</pre> </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> <h3 id="MXBean-spec">MXBean specification</h3>
<p>The MXBean concept provides a simple way to code an MBean <p>The MXBean concept provides a simple way to code an MBean
...@@ -1246,9 +1254,24 @@ public interface Node { ...@@ -1246,9 +1254,24 @@ public interface Node {
@since 1.6 @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 @Documented
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Inherited
public @interface MXBean { public @interface MXBean {
/** /**
True if the annotated interface is an MXBean interface. True if the annotated interface is an MXBean interface.
......
/*
* 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 {
}
/*
* 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;
}
...@@ -91,6 +91,7 @@ class NotQueryExp extends QueryEval implements QueryExp { ...@@ -91,6 +91,7 @@ class NotQueryExp extends QueryEval implements QueryExp {
return "not (" + exp + ")"; return "not (" + exp + ")";
} }
@Override
String toQueryString() { String toQueryString() {
return "not (" + Query.toString(exp) + ")"; return "not (" + Query.toString(exp) + ")";
} }
......
...@@ -58,7 +58,8 @@ import com.sun.jmx.remote.util.ClassLogger; ...@@ -58,7 +58,8 @@ import com.sun.jmx.remote.util.ClassLogger;
* *
* @since 1.5 * @since 1.5
*/ */
public class NotificationBroadcasterSupport implements NotificationEmitter { public class NotificationBroadcasterSupport
implements NotificationEmitter, SendNotification {
/** /**
* Constructs a NotificationBroadcasterSupport where each listener is invoked by the * Constructs a NotificationBroadcasterSupport where each listener is invoked by the
* thread sending the notification. This constructor is equivalent to * thread sending the notification. This constructor is equivalent to
......
/*
* 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 {};
}
/*
* 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();
}
/*
* 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);
}
...@@ -25,6 +25,9 @@ ...@@ -25,6 +25,9 @@
package javax.management; 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 * <p>An MBean whose management interface is determined by reflection
* on a Java interface, and that emits notifications.</p> * on a Java interface, and that emits notifications.</p>
...@@ -62,7 +65,7 @@ package javax.management; ...@@ -62,7 +65,7 @@ package javax.management;
* @since 1.6 * @since 1.6
*/ */
public class StandardEmitterMBean extends StandardMBean public class StandardEmitterMBean extends StandardMBean
implements NotificationEmitter { implements NotificationEmitter, SendNotification {
private final NotificationEmitter emitter; private final NotificationEmitter emitter;
private final MBeanNotificationInfo[] notificationInfo; private final MBeanNotificationInfo[] notificationInfo;
...@@ -76,9 +79,10 @@ public class StandardEmitterMBean extends StandardMBean ...@@ -76,9 +79,10 @@ public class StandardEmitterMBean extends StandardMBean
* for {@code implementation} and {@code emitter} to be the same object.</p> * for {@code implementation} and {@code emitter} to be the same object.</p>
* *
* <p>If {@code emitter} is an instance of {@code * <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 * sendNotification} method will call {@code emitter.}{@link
* NotificationBroadcasterSupport#sendNotification sendNotification}.</p> * SendNotification#sendNotification sendNotification}.</p>
* *
* <p>The array returned by {@link #getNotificationInfo()} on the * <p>The array returned by {@link #getNotificationInfo()} on the
* new MBean is a copy of the array returned by * new MBean is a copy of the array returned by
...@@ -90,20 +94,18 @@ public class StandardEmitterMBean extends StandardMBean ...@@ -90,20 +94,18 @@ public class StandardEmitterMBean extends StandardMBean
* *
* @param implementation the implementation of the MBean interface. * @param implementation the implementation of the MBean interface.
* @param mbeanInterface a Standard 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} * @throws IllegalArgumentException if the {@code mbeanInterface}
* does not follow JMX design patterns for Management Interfaces, or * does not follow JMX design patterns for Management Interfaces, or
* if the given {@code implementation} does not implement the * 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, public <T> StandardEmitterMBean(T implementation, Class<T> mbeanInterface,
NotificationEmitter emitter) { NotificationEmitter emitter) {
super(implementation, mbeanInterface, false); this(implementation, mbeanInterface, false, emitter);
if (emitter == null)
throw new IllegalArgumentException("Null emitter");
this.emitter = emitter;
this.notificationInfo = emitter.getNotificationInfo();
} }
/** /**
...@@ -118,9 +120,10 @@ public class StandardEmitterMBean extends StandardMBean ...@@ -118,9 +120,10 @@ public class StandardEmitterMBean extends StandardMBean
* same object.</p> * same object.</p>
* *
* <p>If {@code emitter} is an instance of {@code * <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 * sendNotification} method will call {@code emitter.}{@link
* NotificationBroadcasterSupport#sendNotification sendNotification}.</p> * SendNotification#sendNotification sendNotification}.</p>
* *
* <p>The array returned by {@link #getNotificationInfo()} on the * <p>The array returned by {@link #getNotificationInfo()} on the
* new MBean is a copy of the array returned by * new MBean is a copy of the array returned by
...@@ -134,21 +137,69 @@ public class StandardEmitterMBean extends StandardMBean ...@@ -134,21 +137,69 @@ public class StandardEmitterMBean extends StandardMBean
* @param mbeanInterface a Standard MBean interface. * @param mbeanInterface a Standard MBean interface.
* @param isMXBean If true, the {@code mbeanInterface} parameter * @param isMXBean If true, the {@code mbeanInterface} parameter
* names an MXBean interface and the resultant MBean is an MXBean. * 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} * @throws IllegalArgumentException if the {@code mbeanInterface}
* does not follow JMX design patterns for Management Interfaces, or * does not follow JMX design patterns for Management Interfaces, or
* if the given {@code implementation} does not implement the * 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, public <T> StandardEmitterMBean(T implementation, Class<T> mbeanInterface,
boolean isMXBean, boolean isMXBean,
NotificationEmitter emitter) { 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) if (emitter == null)
throw new IllegalArgumentException("Null emitter"); emitter = defaultEmitter();
this.emitter = emitter; this.emitter = emitter;
this.notificationInfo = emitter.getNotificationInfo(); this.notificationInfo = emitter.getNotificationInfo();
injectEmitter();
} }
/** /**
...@@ -159,9 +210,10 @@ public class StandardEmitterMBean extends StandardMBean ...@@ -159,9 +210,10 @@ public class StandardEmitterMBean extends StandardMBean
* by forwarding its methods to {@code emitter}.</p> * by forwarding its methods to {@code emitter}.</p>
* *
* <p>If {@code emitter} is an instance of {@code * <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 * sendNotification} method will call {@code emitter.}{@link
* NotificationBroadcasterSupport#sendNotification sendNotification}.</p> * SendNotification#sendNotification sendNotification}.</p>
* *
* <p>The array returned by {@link #getNotificationInfo()} on the * <p>The array returned by {@link #getNotificationInfo()} on the
* new MBean is a copy of the array returned by * new MBean is a copy of the array returned by
...@@ -175,20 +227,17 @@ public class StandardEmitterMBean extends StandardMBean ...@@ -175,20 +227,17 @@ public class StandardEmitterMBean extends StandardMBean
* the given {@code mbeanInterface}.</p> * the given {@code mbeanInterface}.</p>
* *
* @param mbeanInterface a StandardMBean interface. * @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} * @throws IllegalArgumentException if the {@code mbeanInterface}
* does not follow JMX design patterns for Management Interfaces, or * does not follow JMX design patterns for Management Interfaces, or
* if {@code this} does not implement the specified interface, or * if {@code this} does not implement the specified interface.
* if {@code emitter} is null.
*/ */
protected StandardEmitterMBean(Class<?> mbeanInterface, protected StandardEmitterMBean(Class<?> mbeanInterface,
NotificationEmitter emitter) { NotificationEmitter emitter) {
super(mbeanInterface, false); this(mbeanInterface, false, emitter);
if (emitter == null)
throw new IllegalArgumentException("Null emitter");
this.emitter = emitter;
this.notificationInfo = emitter.getNotificationInfo();
} }
/** /**
...@@ -200,9 +249,10 @@ public class StandardEmitterMBean extends StandardMBean ...@@ -200,9 +249,10 @@ public class StandardEmitterMBean extends StandardMBean
* forwarding its methods to {@code emitter}.</p> * forwarding its methods to {@code emitter}.</p>
* *
* <p>If {@code emitter} is an instance of {@code * <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 * sendNotification} method will call {@code emitter.}{@link
* NotificationBroadcasterSupport#sendNotification sendNotification}.</p> * SendNotification#sendNotification sendNotification}.</p>
* *
* <p>The array returned by {@link #getNotificationInfo()} on the * <p>The array returned by {@link #getNotificationInfo()} on the
* new MBean is a copy of the array returned by * new MBean is a copy of the array returned by
...@@ -218,20 +268,86 @@ public class StandardEmitterMBean extends StandardMBean ...@@ -218,20 +268,86 @@ public class StandardEmitterMBean extends StandardMBean
* @param mbeanInterface a StandardMBean interface. * @param mbeanInterface a StandardMBean interface.
* @param isMXBean If true, the {@code mbeanInterface} parameter * @param isMXBean If true, the {@code mbeanInterface} parameter
* names an MXBean interface and the resultant MBean is an MXBean. * 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} * @throws IllegalArgumentException if the {@code mbeanInterface}
* does not follow JMX design patterns for Management Interfaces, or * does not follow JMX design patterns for Management Interfaces, or
* if {@code this} does not implement the specified interface, or * if {@code this} does not implement the specified interface.
* if {@code emitter} is null.
*/ */
protected StandardEmitterMBean(Class<?> mbeanInterface, boolean isMXBean, protected StandardEmitterMBean(Class<?> mbeanInterface, boolean isMXBean,
NotificationEmitter emitter) { 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) if (emitter == null)
throw new IllegalArgumentException("Null emitter"); emitter = defaultEmitter();
this.emitter = emitter; this.emitter = emitter;
this.notificationInfo = emitter.getNotificationInfo(); 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) public void removeNotificationListener(NotificationListener listener)
...@@ -259,10 +375,10 @@ public class StandardEmitterMBean extends StandardMBean ...@@ -259,10 +375,10 @@ public class StandardEmitterMBean extends StandardMBean
/** /**
* <p>Sends a notification.</p> * <p>Sends a notification.</p>
* *
* <p>If the {@code emitter} parameter to the constructor was an * <p>If the {@code emitter} parameter to the constructor was
* instance of {@code NotificationBroadcasterSupport} then this * an instance of {@link SendNotification}, such as {@link
* method will call {@code emitter.}{@link * NotificationBroadcasterSupport}, then this method will call {@code
* NotificationBroadcasterSupport#sendNotification * emitter.}{@link SendNotification#sendNotification
* sendNotification}.</p> * sendNotification}.</p>
* *
* @param n the notification to send. * @param n the notification to send.
...@@ -271,13 +387,12 @@ public class StandardEmitterMBean extends StandardMBean ...@@ -271,13 +387,12 @@ public class StandardEmitterMBean extends StandardMBean
* constructor was not a {@code NotificationBroadcasterSupport}. * constructor was not a {@code NotificationBroadcasterSupport}.
*/ */
public void sendNotification(Notification n) { public void sendNotification(Notification n) {
if (emitter instanceof NotificationBroadcasterSupport) if (emitter instanceof SendNotification)
((NotificationBroadcasterSupport) emitter).sendNotification(n); ((SendNotification) emitter).sendNotification(n);
else { else {
final String msg = final String msg =
"Cannot sendNotification when emitter is not an " + "Cannot sendNotification when emitter is not an " +
"instance of NotificationBroadcasterSupport: " + "instance of SendNotification: " + emitter.getClass().getName();
emitter.getClass().getName();
throw new ClassCastException(msg); throw new ClassCastException(msg);
} }
} }
...@@ -292,6 +407,7 @@ public class StandardEmitterMBean extends StandardMBean ...@@ -292,6 +407,7 @@ public class StandardEmitterMBean extends StandardMBean
* @param info The default MBeanInfo derived by reflection. * @param info The default MBeanInfo derived by reflection.
* @return the MBeanNotificationInfo[] for the new MBeanInfo. * @return the MBeanNotificationInfo[] for the new MBeanInfo.
*/ */
@Override
MBeanNotificationInfo[] getNotifications(MBeanInfo info) { MBeanNotificationInfo[] getNotifications(MBeanInfo info) {
return getNotificationInfo(); return getNotificationInfo();
} }
......
...@@ -27,6 +27,7 @@ package javax.management; ...@@ -27,6 +27,7 @@ package javax.management;
import com.sun.jmx.mbeanserver.DescriptorCache; import com.sun.jmx.mbeanserver.DescriptorCache;
import com.sun.jmx.mbeanserver.Introspector; import com.sun.jmx.mbeanserver.Introspector;
import com.sun.jmx.mbeanserver.MBeanInjector;
import com.sun.jmx.mbeanserver.MBeanSupport; import com.sun.jmx.mbeanserver.MBeanSupport;
import com.sun.jmx.mbeanserver.MXBeanSupport; import com.sun.jmx.mbeanserver.MXBeanSupport;
import com.sun.jmx.mbeanserver.StandardMBeanSupport; import com.sun.jmx.mbeanserver.StandardMBeanSupport;
...@@ -125,7 +126,78 @@ import static javax.management.JMX.MBeanOptions; ...@@ -125,7 +126,78 @@ import static javax.management.JMX.MBeanOptions;
* *
* @since 1.5 * @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 = private final static DescriptorCache descriptors =
DescriptorCache.getInstance(JMX.proof); DescriptorCache.getInstance(JMX.proof);
...@@ -347,7 +419,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration { ...@@ -347,7 +419,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
* the management interface associated with the given * the management interface associated with the given
* implementation. * implementation.
* @param options MBeanOptions that control the operation of the resulting * @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 * @param <T> Allows the compiler to check
* that {@code implementation} does indeed implement the class * that {@code implementation} does indeed implement the class
* described by {@code mbeanInterface}. The compiler can only * described by {@code mbeanInterface}. The compiler can only
...@@ -381,7 +453,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration { ...@@ -381,7 +453,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
* @param mbeanInterface The Management Interface exported by this * @param mbeanInterface The Management Interface exported by this
* MBean. * MBean.
* @param options MBeanOptions that control the operation of the resulting * @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> * @exception IllegalArgumentException if the <var>mbeanInterface</var>
* does not follow JMX design patterns for Management Interfaces, or * does not follow JMX design patterns for Management Interfaces, or
...@@ -441,7 +513,67 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration { ...@@ -441,7 +513,67 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
* @see #setImplementation * @see #setImplementation
**/ **/
public Object getImplementation() { 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 { ...@@ -457,7 +589,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
* @return The class of the implementation of this Standard MBean (or MXBean). * @return The class of the implementation of this Standard MBean (or MXBean).
**/ **/
public Class<?> getImplementationClass() { public Class<?> getImplementationClass() {
return mbean.getResource().getClass(); return mbean.getWrappedObject().getClass();
} }
/** /**
...@@ -559,7 +691,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration { ...@@ -559,7 +691,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
MBeanSupport msupport = mbean; MBeanSupport msupport = mbean;
final MBeanInfo bi = msupport.getMBeanInfo(); final MBeanInfo bi = msupport.getMBeanInfo();
final Object impl = msupport.getResource(); final Object impl = msupport.getWrappedObject();
final boolean immutableInfo = immutableInfo(this.getClass()); final boolean immutableInfo = immutableInfo(this.getClass());
...@@ -1184,6 +1316,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration { ...@@ -1184,6 +1316,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration {
public ObjectName preRegister(MBeanServer server, ObjectName name) public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception { throws Exception {
mbean.register(server, name); mbean.register(server, name);
MBeanInjector.inject(mbean.getWrappedObject(), server, name);
return name; return name;
} }
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
* have any questions. * have any questions.
*/ */
/* /*
* @author IBM Corp. * @(#)author IBM Corp.
* *
* Copyright IBM Corp. 1999-2000. All rights reserved. * Copyright IBM Corp. 1999-2000. All rights reserved.
*/ */
...@@ -55,6 +55,7 @@ import javax.management.AttributeChangeNotificationFilter; ...@@ -55,6 +55,7 @@ import javax.management.AttributeChangeNotificationFilter;
import javax.management.AttributeList; import javax.management.AttributeList;
import javax.management.AttributeNotFoundException; import javax.management.AttributeNotFoundException;
import javax.management.Descriptor; import javax.management.Descriptor;
import javax.management.DynamicWrapperMBean;
import javax.management.InstanceNotFoundException; import javax.management.InstanceNotFoundException;
import javax.management.InvalidAttributeValueException; import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException; import javax.management.ListenerNotFoundException;
...@@ -115,7 +116,7 @@ import sun.reflect.misc.ReflectUtil; ...@@ -115,7 +116,7 @@ import sun.reflect.misc.ReflectUtil;
*/ */
public class RequiredModelMBean public class RequiredModelMBean
implements ModelMBean, MBeanRegistration, NotificationEmitter { implements ModelMBean, MBeanRegistration, NotificationEmitter, DynamicWrapperMBean {
/*************************************/ /*************************************/
/* attributes */ /* attributes */
...@@ -133,6 +134,9 @@ public class RequiredModelMBean ...@@ -133,6 +134,9 @@ public class RequiredModelMBean
* and operations will be executed */ * and operations will be executed */
private Object managedResource = null; private Object managedResource = null;
/* true if getWrappedObject returns the wrapped resource */
private boolean visible;
/* records the registering in MBeanServer */ /* records the registering in MBeanServer */
private boolean registered = false; private boolean registered = false;
private transient MBeanServer server = null; private transient MBeanServer server = null;
...@@ -318,9 +322,13 @@ public class RequiredModelMBean ...@@ -318,9 +322,13 @@ public class RequiredModelMBean
* *
* @param mr Object that is the managed resource * @param mr Object that is the managed resource
* @param mr_type The type of reference for the managed resource. * @param mr_type The type of reference for the managed resource.
* <br>Can be: "ObjectReference", "Handle", "IOR", "EJBHandle", * <br>Can be: "ObjectReference", "VisibleObjectReference",
* or "RMIReference". * "Handle", "IOR", "EJBHandle", or "RMIReference".
* <br>In this implementation only "ObjectReference" is supported. * <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 * @exception MBeanException The initializer of the object has
* thrown an exception. * thrown an exception.
...@@ -340,10 +348,11 @@ public class RequiredModelMBean ...@@ -340,10 +348,11 @@ public class RequiredModelMBean
"setManagedResource(Object,String)","Entry"); "setManagedResource(Object,String)","Entry");
} }
visible = "visibleObjectReference".equalsIgnoreCase(mr_type);
// check that the mr_type is supported by this JMXAgent // check that the mr_type is supported by this JMXAgent
// only "objectReference" is supported // only "objectReference" is supported
if ((mr_type == null) || if (!"objectReference".equalsIgnoreCase(mr_type) && !visible) {
(! mr_type.equalsIgnoreCase("objectReference"))) {
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER, MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(), RequiredModelMBean.class.getName(),
...@@ -368,6 +377,51 @@ public class RequiredModelMBean ...@@ -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 * <p>Instantiates this MBean instance with the data found for
* the MBean in the persistent store. The data loaded could include * the MBean in the persistent store. The data loaded could include
......
...@@ -38,14 +38,17 @@ have any questions. ...@@ -38,14 +38,17 @@ have any questions.
so within the access control context of the so within the access control context of the
{@link javax.management.monitor.Monitor#start} caller.</p> {@link javax.management.monitor.Monitor#start} caller.</p>
<p>The value being monitored can be a simple value contained within a <p id="complex">The value being monitored can be a simple value
complex type. For example, the {@link java.lang.management.MemoryMXBean contained within a complex type. For example, the {@link
MemoryMXBean} defined in <tt>java.lang.management</tt> has an attribute java.lang.management.MemoryMXBean MemoryMXBean} defined in
<tt>HeapMemoryUsage</tt> of type {@link java.lang.management.MemoryUsage <tt>java.lang.management</tt> has an attribute
MemoryUsage}. To monitor the amount of <i>used</i> memory, described by <tt>HeapMemoryUsage</tt> of type {@link
the <tt>used</tt> property of <tt>MemoryUsage</tt>, you could monitor java.lang.management.MemoryUsage MemoryUsage}. To monitor the
"<tt>HeapMemoryUsage.used</tt>". That string would be the argument to amount of <i>used</i> memory, described by the <tt>used</tt>
{@link javax.management.monitor.MonitorMBean#setObservedAttribute(String) 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> setObservedAttribute}.</p>
<p>The rules used to interpret an <tt>ObservedAttribute</tt> like <p>The rules used to interpret an <tt>ObservedAttribute</tt> like
......
...@@ -142,18 +142,20 @@ abstract class SelectorImpl ...@@ -142,18 +142,20 @@ abstract class SelectorImpl
// Precondition: Synchronized on this, keys, and selectedKeys // Precondition: Synchronized on this, keys, and selectedKeys
Set cks = cancelledKeys(); Set cks = cancelledKeys();
synchronized (cks) { synchronized (cks) {
Iterator i = cks.iterator(); if (!cks.isEmpty()) {
while (i.hasNext()) { Iterator i = cks.iterator();
SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); while (i.hasNext()) {
try { SelectionKeyImpl ski = (SelectionKeyImpl)i.next();
implDereg(ski); try {
} catch (SocketException se) { implDereg(ski);
IOException ioe = new IOException( } catch (SocketException se) {
"Error deregistering key"); IOException ioe = new IOException(
ioe.initCause(se); "Error deregistering key");
throw ioe; ioe.initCause(se);
} finally { throw ioe;
i.remove(); } finally {
i.remove();
}
} }
} }
} }
......
...@@ -25,7 +25,8 @@ ...@@ -25,7 +25,8 @@
* @test * @test
* @bug 6230699 * @bug 6230699
* @summary Test ThreadReference.ownedMonitorsAndFrames() * @summary Test ThreadReference.ownedMonitorsAndFrames()
* * @bug 6701700
* @summary MonitorInfo objects aren't invalidated when the owning thread is resumed
* @author Swamy Venkataramanappa * @author Swamy Venkataramanappa
* *
* @run build TestScaffold VMConnection TargetListener TargetAdapter * @run build TestScaffold VMConnection TargetListener TargetAdapter
...@@ -100,15 +101,15 @@ public class MonitorFrameInfo extends TestScaffold { ...@@ -100,15 +101,15 @@ public class MonitorFrameInfo extends TestScaffold {
if (!mainThread.frame(0).location().method().name() if (!mainThread.frame(0).location().method().name()
.equals("foo3")) { .equals("foo3")) {
failure("frame failed"); failure("FAILED: frame failed");
} }
if (mainThread.frames().size() != (initialSize + 3)) { if (mainThread.frames().size() != (initialSize + 3)) {
failure("frames size failed"); failure("FAILED: frames size failed");
} }
if (mainThread.frames().size() != mainThread.frameCount()) { if (mainThread.frames().size() != mainThread.frameCount()) {
failure("frames size not equal to frameCount"); failure("FAILED: frames size not equal to frameCount");
} }
/* Test monitor frame info. /* Test monitor frame info.
...@@ -119,13 +120,32 @@ public class MonitorFrameInfo extends TestScaffold { ...@@ -119,13 +120,32 @@ public class MonitorFrameInfo extends TestScaffold {
if (monitors.size() != expectedCount) { if (monitors.size() != expectedCount) {
failure("monitors count is not equal to expected count"); failure("monitors count is not equal to expected count");
} }
MonitorInfo mon = null;
for (int j=0; j < monitors.size(); j++) { for (int j=0; j < monitors.size(); j++) {
MonitorInfo mon = (MonitorInfo)monitors.get(j); mon = (MonitorInfo)monitors.get(j);
System.out.println("Monitor obj " + mon.monitor() + "depth =" +mon.stackDepth()); System.out.println("Monitor obj " + mon.monitor() + "depth =" +mon.stackDepth());
if (mon.stackDepth() != expectedDepth[j]) { if (mon.stackDepth() != expectedDepth[j]) {
failure("monitor stack depth is not equal to expected depth"); failure("FAILED: monitor stack depth is not equal to expected depth");
} }
} }
// The last gotten monInfo is in mon. When we resume the thread,
// it should become invalid. We will step out of the top frame
// so that the frame depth in this mon object will no longer be correct.
// That is why the monInfo's have to become invalid when the thread is
// resumed.
stepOut(mainThread);
boolean ok = false;
try {
System.out.println("*** Saved Monitor obj " + mon.monitor() + "depth =" +mon.stackDepth());
} catch(InvalidStackFrameException ee) {
// ok
ok = true;
System.out.println("Got expected InvalidStackFrameException after a resume");
}
if (!ok) {
failure("FAILED: MonitorInfo object was not invalidated by a resume");
}
} else { } else {
System.out.println("can not get monitors frame info"); System.out.println("can not get monitors frame info");
} }
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册