From 72d83e37086bba97e06498d1b17d13a8e7d76085 Mon Sep 17 00:00:00 2001 From: Swamy Venkataramanappa Date: Wed, 25 Jun 2008 16:33:59 -0700 Subject: [PATCH 01/21] 6614556: null location for MonitorContendedEnterEvent Reviewed-by: jjh --- jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java b/jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java index 1a2c61c7bc9..82a7c78085a 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java @@ -208,8 +208,9 @@ public class EventSetImpl extends ArrayList implements EventSet { } public String toString() { - return eventName() + "@" + location().toString() + - " in thread " + thread().name(); + return eventName() + "@" + + ((location() == null) ? " null" : location().toString()) + + " in thread " + thread().name(); } } From 1a0c261469247afef0bcf8b05109bbdef317412f Mon Sep 17 00:00:00 2001 From: Jim Holmlund Date: Tue, 1 Jul 2008 09:23:00 -0700 Subject: [PATCH 02/21] 2157677: ClassPrepareRequest.addSourceNameFilter() does not behave as documented Add proper handling of JVMTI errors. Reviewed-by: tbell --- jdk/src/share/back/eventFilter.c | 15 ++++---- .../com/sun/jdi/SourceNameFilterTest.java | 35 +++++++++++++++---- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/jdk/src/share/back/eventFilter.c b/jdk/src/share/back/eventFilter.c index edddeba808c..955cf8813b4 100644 --- a/jdk/src/share/back/eventFilter.c +++ b/jdk/src/share/back/eventFilter.c @@ -492,14 +492,17 @@ eventFilterRestricted_passesFilter(JNIEnv *env, char *sourceName = 0; jvmtiError error = JVMTI_FUNC_PTR(gdata->jvmti,GetSourceFileName) (gdata->jvmti, clazz, &sourceName); - if (error == JVMTI_ERROR_NONE) { - if (sourceName == 0 || !patternStringMatch(sourceName, desiredNamePattern)) { - /* We have no match */ - jvmtiDeallocate(sourceName); - return JNI_FALSE; - } + if (error == JVMTI_ERROR_NONE && + sourceName != 0 && + patternStringMatch(sourceName, desiredNamePattern)) { + // got a hit - report the event + 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); + return JNI_FALSE; } break; } diff --git a/jdk/test/com/sun/jdi/SourceNameFilterTest.java b/jdk/test/com/sun/jdi/SourceNameFilterTest.java index 1565a8886a8..cb8260646f0 100644 --- a/jdk/test/com/sun/jdi/SourceNameFilterTest.java +++ b/jdk/test/com/sun/jdi/SourceNameFilterTest.java @@ -23,7 +23,7 @@ /** * @test - * @bug 4836939 + * @bug 4836939 6646613 * @summary JDI add addSourceNameFilter to ClassPrepareRequest * * @author jjh @@ -31,7 +31,11 @@ * @run build TestScaffold VMConnection TargetListener TargetAdapter * @run compile -g SourceNameFilterTest.java * @run main SourceNameFilterTest + * @run compile -g:none SourceNameFilterTest.java + * @run main SourceNameFilterTest */ +// The compile -g:none suppresses the lineNumber table to trigger bug 6646613. + import com.sun.jdi.*; import com.sun.jdi.event.*; import com.sun.jdi.request.*; @@ -84,7 +88,6 @@ public class SourceNameFilterTest extends TestScaffold { boolean gotEvent1 = false; boolean gotEvent2 = false; boolean gotEvent3 = false; - ClassPrepareRequest cpReq; boolean shouldResume = false; SourceNameFilterTest (String args[]) { @@ -151,6 +154,18 @@ public class SourceNameFilterTest extends TestScaffold { */ BreakpointEvent bpe = startToMain("SourceNameFilterTarg"); targetClass = bpe.location().declaringType(); + boolean noSourceName = false; + try { + targetClass.sourceName(); + } catch (AbsentInformationException ee) { + noSourceName = true; + } + if (noSourceName) { + println("-- Running with no source names"); + } else { + println("-- Running with source names"); + } + mainThread = bpe.thread(); EventRequestManager erm = vm().eventRequestManager(); addListener(this); @@ -175,7 +190,9 @@ public class SourceNameFilterTest extends TestScaffold { /* * This should cause us to get a class prepare event for - * LoadedLater3 + * LoadedLater3 except in the case where -g:none + * was used to compile so that there is no LineNumberTable + * and therefore, no source name for the class. */ cpReq = erm.createClassPrepareRequest(); cpReq.addSourceNameFilter("SourceNameFilterTest.java"); @@ -186,17 +203,21 @@ public class SourceNameFilterTest extends TestScaffold { if (!gotEvent1) { failure("failure: Did not get a class prepare request " + - "for Loadedlater1"); + "for LoadedLater1"); } if (gotEvent2) { failure("failure: Did get a class prepare request " + - "for Loadedlater2"); + "for LoadedLater2"); } - if (!gotEvent3) { + if (gotEvent3 && noSourceName) { + failure("failure: Did get a class prepare request " + + "for LoadedLater3"); + } + else if (!gotEvent3 && !noSourceName) { failure("failure: Did not get a class prepare request " + - "for Loadedlater3"); + "for LoadedLater3"); } /* From 5967d518b5efbfe387b3d510eee178329b341346 Mon Sep 17 00:00:00 2001 From: Eamonn McManus Date: Fri, 4 Jul 2008 18:55:37 +0200 Subject: [PATCH 03/21] 6601652: MXBeans: no IllegalArgumentException in the ex. chain for SortedSet/Map with a non-null comparator() Forward-port this bug fix from JDK 6 Reviewed-by: dfuchs, lmalvent --- .../DefaultMXBeanMappingFactory.java | 4 +- .../mxbean/ComparatorExceptionTest.java | 90 +++++++++++++++++++ .../javax/management/mxbean/MXBeanTest.java | 37 +++++--- .../mxbean/SameObjectTwoNamesTest.java | 76 ++++++++++++++++ 4 files changed, 194 insertions(+), 13 deletions(-) create mode 100644 jdk/test/javax/management/mxbean/ComparatorExceptionTest.java create mode 100644 jdk/test/javax/management/mxbean/SameObjectTwoNamesTest.java diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java index 1a58621458a..0e15a464f31 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java @@ -686,7 +686,7 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { final String msg = "Cannot convert SortedSet with non-null comparator: " + comparator; - throw new OpenDataException(msg); + throw openDataException(msg, new IllegalArgumentException(msg)); } } final Object[] openArray = (Object[]) @@ -800,7 +800,7 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { final String msg = "Cannot convert SortedMap with non-null comparator: " + comparator; - throw new OpenDataException(msg); + throw openDataException(msg, new IllegalArgumentException(msg)); } } final TabularType tabularType = (TabularType) getOpenType(); diff --git a/jdk/test/javax/management/mxbean/ComparatorExceptionTest.java b/jdk/test/javax/management/mxbean/ComparatorExceptionTest.java new file mode 100644 index 00000000000..cd88a161b02 --- /dev/null +++ b/jdk/test/javax/management/mxbean/ComparatorExceptionTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6601652 + * @summary Test exception when SortedMap or SortedSet has non-null Comparator + * @author Eamonn McManus + */ + +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; + +public class ComparatorExceptionTest { + public static interface TestMXBean { + public SortedSet getSortedSet(); + public SortedMap getSortedMap(); + } + + public static class TestImpl implements TestMXBean { + public SortedSet getSortedSet() { + return new TreeSet(String.CASE_INSENSITIVE_ORDER); + } + + public SortedMap getSortedMap() { + return new TreeMap(String.CASE_INSENSITIVE_ORDER); + } + } + + private static String failure; + + private static void fail(String why) { + failure = "FAILED: " + why; + System.out.println(failure); + } + + public static void main(String[] args) throws Exception { + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + ObjectName name = new ObjectName("a:b=c"); + mbs.registerMBean(new TestImpl(), name); + + for (String attr : new String[] {"SortedSet", "SortedMap"}) { + try { + Object value = mbs.getAttribute(name, attr); + fail("get " + attr + " did not throw exception"); + } catch (Exception e) { + Throwable t = e; + while (!(t instanceof IllegalArgumentException)) { + if (t == null) + break; + t = t.getCause(); + } + if (t != null) + System.out.println("Correct exception for " + attr); + else { + fail("get " + attr + " got wrong exception"); + e.printStackTrace(System.out); + } + } + } + + if (failure != null) + throw new Exception(failure); + } +} diff --git a/jdk/test/javax/management/mxbean/MXBeanTest.java b/jdk/test/javax/management/mxbean/MXBeanTest.java index 9abcf57e450..9415b39faef 100644 --- a/jdk/test/javax/management/mxbean/MXBeanTest.java +++ b/jdk/test/javax/management/mxbean/MXBeanTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6175517 6278707 6318827 6305746 6392303 + * @bug 6175517 6278707 6318827 6305746 6392303 6600709 * @summary General MXBean test. * @author Eamonn McManus * @run clean MXBeanTest MerlinMXBean TigerMXBean @@ -40,7 +40,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; -import javax.management.Attribute; +import java.util.Map; +import java.util.SortedMap; import javax.management.JMX; import javax.management.MBeanAttributeInfo; import javax.management.MBeanInfo; @@ -55,10 +56,6 @@ import javax.management.StandardMBean; import javax.management.openmbean.ArrayType; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataInvocationHandler; -import javax.management.openmbean.OpenMBeanAttributeInfo; -import javax.management.openmbean.OpenMBeanInfo; -import javax.management.openmbean.OpenMBeanOperationInfo; -import javax.management.openmbean.OpenMBeanParameterInfo; import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; @@ -81,10 +78,8 @@ public class MXBeanTest { if (failures == 0) System.out.println("Test passed"); - else { - System.out.println("TEST FAILURES: " + failures); - System.exit(1); - } + else + throw new Exception("TEST FAILURES: " + failures); } private static int failures = 0; @@ -561,6 +556,11 @@ public class MXBeanTest { return false; return deepEqual(o1, o2, namedMXBeans); } + if (o1 instanceof Map) { + if (!(o2 instanceof Map)) + return false; + return equalMap((Map) o1, (Map) o2, namedMXBeans); + } if (o1 instanceof CompositeData && o2 instanceof CompositeData) { return compositeDataEqual((CompositeData) o1, (CompositeData) o2, namedMXBeans); @@ -600,6 +600,21 @@ public class MXBeanTest { return true; } + private static boolean equalMap(Map m1, Map m2, + NamedMXBeans namedMXBeans) { + if (m1.size() != m2.size()) + return false; + if ((m1 instanceof SortedMap) != (m2 instanceof SortedMap)) + return false; + for (Object k1 : m1.keySet()) { + if (!m2.containsKey(k1)) + return false; + if (!equal(m1.get(k1), m2.get(k1), namedMXBeans)) + return false; + } + return true; + } + // This is needed to work around a bug (5095277) // in CompositeDataSupport.equals private static boolean compositeDataEqual(CompositeData cd1, @@ -655,7 +670,7 @@ public class MXBeanTest { /* I wanted to call this method toString(Object), but oddly enough this meant that I couldn't call it from the inner class MXBeanImplInvocationHandler, because the inherited Object.toString() - prevented that. Surprising behaviour. */ + prevented that. */ static String string(Object o) { if (o == null) return "null"; diff --git a/jdk/test/javax/management/mxbean/SameObjectTwoNamesTest.java b/jdk/test/javax/management/mxbean/SameObjectTwoNamesTest.java new file mode 100644 index 00000000000..53cc9bb3b3d --- /dev/null +++ b/jdk/test/javax/management/mxbean/SameObjectTwoNamesTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test SameObjectTwoNamesTest.java + * @bug 6283873 + * @summary Check that registering the same MXBean under two different + * names produces an exception + * @author Alexander Shusherov + * @author Eamonn McManus + * @run main SameObjectTwoNamesTest + * @run main/othervm -Djmx.mxbean.multiname=true SameObjectTwoNamesTest + */ + +import javax.management.InstanceAlreadyExistsException; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; + +public class SameObjectTwoNamesTest { + + public static void main(String[] args) throws Exception { + boolean expectException = + (System.getProperty("jmx.mxbean.multiname") == null); + try { + ObjectName objectName1 = new ObjectName("test:index=1"); + ObjectName objectName2 = new ObjectName("test:index=2"); + MBeanServer mbs = MBeanServerFactory.createMBeanServer(); + MXBC_SimpleClass01 mxBeanObject = new MXBC_SimpleClass01(); + + mbs.registerMBean(mxBeanObject, objectName1); + + mbs.registerMBean(mxBeanObject, objectName2); + + if (expectException) { + throw new Exception("TEST FAILED: " + + "InstanceAlreadyExistsException was not thrown"); + } else + System.out.println("Correctly got no exception with compat property"); + } catch (InstanceAlreadyExistsException e) { + if (expectException) { + System.out.println("Got expected InstanceAlreadyExistsException:"); + e.printStackTrace(System.out); + } else { + throw new Exception( + "TEST FAILED: Got exception even though compat property set", e); + } + } + System.out.println("TEST PASSED"); + } + + public interface MXBC_Simple01MXBean {} + + public static class MXBC_SimpleClass01 implements MXBC_Simple01MXBean {} + +} From 4333dd3520136a8d9644f928699bc115308abaf3 Mon Sep 17 00:00:00 2001 From: Eamonn McManus Date: Wed, 9 Jul 2008 10:36:07 +0200 Subject: [PATCH 04/21] 6323980: Annotations to simplify MBean development Reviewed-by: jfdenise, dfuchs --- .../DefaultMBeanServerInterceptor.java | 519 +++++++---- .../sun/jmx/mbeanserver/DynamicMBean2.java | 14 +- .../com/sun/jmx/mbeanserver/Introspector.java | 229 +++-- .../sun/jmx/mbeanserver/MBeanAnalyzer.java | 29 +- .../sun/jmx/mbeanserver/MBeanInjector.java | 291 ++++++ .../jmx/mbeanserver/MBeanIntrospector.java | 161 +++- .../com/sun/jmx/mbeanserver/MBeanSupport.java | 6 +- .../jmx/mbeanserver/MXBeanIntrospector.java | 32 +- .../sun/jmx/mbeanserver/MXBeanSupport.java | 4 +- .../sun/jmx/mbeanserver/NotifySupport.java | 186 ++++ .../com/sun/jmx/mbeanserver/Repository.java | 161 +++- .../StandardMBeanIntrospector.java | 31 +- .../jmx/mbeanserver/StandardMBeanSupport.java | 43 +- .../classes/com/sun/jmx/mbeanserver/Util.java | 5 + .../javax/management/BinaryRelQueryExp.java | 1 + .../classes/javax/management/Description.java | 180 ++++ .../classes/javax/management/Descriptor.java | 23 +- .../javax/management/DescriptorFields.java | 137 +++ .../javax/management/DescriptorKey.java | 52 +- .../javax/management/DynamicWrapperMBean.java | 62 ++ .../classes/javax/management/Impact.java | 105 +++ .../share/classes/javax/management/JMX.java | 26 + .../share/classes/javax/management/MBean.java | 68 ++ .../javax/management/MBeanOperationInfo.java | 40 +- .../javax/management/MBeanRegistration.java | 104 ++- .../classes/javax/management/MBeanServer.java | 11 +- .../management/MBeanServerConnection.java | 6 + .../classes/javax/management/MXBean.java | 31 +- .../javax/management/ManagedAttribute.java | 64 ++ .../javax/management/ManagedOperation.java | 67 ++ .../classes/javax/management/NotQueryExp.java | 1 + .../NotificationBroadcasterSupport.java | 3 +- .../javax/management/NotificationInfo.java | 117 +++ .../javax/management/NotificationInfos.java | 72 ++ .../javax/management/SendNotification.java | 38 + .../management/StandardEmitterMBean.java | 198 ++++- .../javax/management/StandardMBean.java | 145 ++- .../modelmbean/RequiredModelMBean.java | 68 +- .../javax/management/monitor/package.html | 19 +- .../classes/javax/management/package.html | 415 +++++---- .../Introspector/AnnotatedMBeanTest.java | 337 +++++++ .../AnnotatedNotificationInfoTest.java | 271 ++++++ .../Introspector/MBeanDescriptionTest.java | 830 ++++++++++++++++++ .../Introspector/ParameterNameTest.java | 116 +++ .../Introspector/ResourceInjectionTest.java | 656 ++++++++++++++ .../management/Introspector/annot/Name.java | 32 + 46 files changed, 5419 insertions(+), 587 deletions(-) create mode 100644 jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java create mode 100644 jdk/src/share/classes/com/sun/jmx/mbeanserver/NotifySupport.java create mode 100644 jdk/src/share/classes/javax/management/Description.java create mode 100644 jdk/src/share/classes/javax/management/DescriptorFields.java create mode 100644 jdk/src/share/classes/javax/management/DynamicWrapperMBean.java create mode 100644 jdk/src/share/classes/javax/management/Impact.java create mode 100644 jdk/src/share/classes/javax/management/MBean.java create mode 100644 jdk/src/share/classes/javax/management/ManagedAttribute.java create mode 100644 jdk/src/share/classes/javax/management/ManagedOperation.java create mode 100644 jdk/src/share/classes/javax/management/NotificationInfo.java create mode 100644 jdk/src/share/classes/javax/management/NotificationInfos.java create mode 100644 jdk/src/share/classes/javax/management/SendNotification.java create mode 100644 jdk/test/javax/management/Introspector/AnnotatedMBeanTest.java create mode 100644 jdk/test/javax/management/Introspector/AnnotatedNotificationInfoTest.java create mode 100644 jdk/test/javax/management/Introspector/MBeanDescriptionTest.java create mode 100644 jdk/test/javax/management/Introspector/ParameterNameTest.java create mode 100644 jdk/test/javax/management/Introspector/ResourceInjectionTest.java create mode 100644 jdk/test/javax/management/Introspector/annot/Name.java diff --git a/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java b/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java index b968ba9aa1e..4d47f012f4e 100644 --- a/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java +++ b/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java @@ -84,8 +84,13 @@ import com.sun.jmx.mbeanserver.MBeanInstantiator; import com.sun.jmx.mbeanserver.Repository; import com.sun.jmx.mbeanserver.NamedObject; import com.sun.jmx.mbeanserver.Introspector; +import com.sun.jmx.mbeanserver.MBeanInjector; +import com.sun.jmx.mbeanserver.NotifySupport; +import com.sun.jmx.mbeanserver.Repository.RegistrationContext; import com.sun.jmx.mbeanserver.Util; import com.sun.jmx.remote.util.EnvHelp; +import javax.management.DynamicWrapperMBean; +import javax.management.NotificationBroadcasterSupport; /** * This is the default class for MBean manipulation on the agent side. It @@ -433,36 +438,26 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { if (instance instanceof MBeanRegistration) preDeregisterInvoke((MBeanRegistration) instance); - repository.remove(name); - // may throw InstanceNotFoundException + final Object resource = getResource(instance); - /** - * Checks if the unregistered MBean is a ClassLoader - * If so, it removes the MBean from the default loader repository. - */ + // Unregisters the MBean from the repository. + // Returns the resource context that was used. + // The returned context does nothing for regular MBeans. + // For ClassLoader MBeans and JMXNamespace (and JMXDomain) + // MBeans - the context makes it possible to unregister these + // objects from the appropriate framework artifacts, such as + // the CLR or the dispatcher, from within the repository lock. + // In case of success, we also need to call context.done() at the + // end of this method. + // + final ResourceContext context = + unregisterFromRepository(resource, instance, name); - Object resource = getResource(instance); - if (resource instanceof ClassLoader - && resource != server.getClass().getClassLoader()) { - final ModifiableClassLoaderRepository clr = - instantiator.getClassLoaderRepository(); - if (clr != null) clr.removeClassLoader(name); - } - - // --------------------- - // Send deletion event - // --------------------- - if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { - MBEANSERVER_LOGGER.logp(Level.FINER, - DefaultMBeanServerInterceptor.class.getName(), - "unregisterMBean", "Send delete notification of object " + - name.getCanonicalName()); - } - sendNotification(MBeanServerNotification.UNREGISTRATION_NOTIFICATION, - name); if (instance instanceof MBeanRegistration) postDeregisterInvoke((MBeanRegistration) instance); + + context.done(); } public ObjectInstance getObjectInstance(ObjectName name) @@ -939,15 +934,22 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } ObjectName logicalName = name; + logicalName = preRegister(mbean, server, name); + + // preRegister returned successfully, so from this point on we + // must call postRegister(false) if there is any problem. + boolean registered = false; + boolean registerFailed = false; + ResourceContext context = null; + + try { + mbean = injectResources(mbean, server, logicalName); - if (mbean instanceof MBeanRegistration) { - MBeanRegistration reg = (MBeanRegistration) mbean; - logicalName = preRegisterInvoke(reg, name, server); if (mbean instanceof DynamicMBean2) { try { ((DynamicMBean2) mbean).preRegister2(server, logicalName); + registerFailed = true; // until we succeed } catch (Exception e) { - postRegisterInvoke(reg, false, false); if (e instanceof RuntimeException) throw (RuntimeException) e; if (e instanceof InstanceAlreadyExistsException) @@ -960,86 +962,102 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { logicalName = ObjectName.getInstance(nonDefaultDomain(logicalName)); } - } - checkMBeanPermission(classname, null, logicalName, "registerMBean"); + checkMBeanPermission(classname, null, logicalName, "registerMBean"); - final ObjectInstance result; - if (logicalName!=null) { - result = new ObjectInstance(logicalName, classname); - internal_addObject(mbean, logicalName); - } else { - if (mbean instanceof MBeanRegistration) - postRegisterInvoke((MBeanRegistration) mbean, false, true); - final RuntimeException wrapped = - new IllegalArgumentException("No object name specified"); - throw new RuntimeOperationsException(wrapped, - "Exception occurred trying to register the MBean"); - } - - if (mbean instanceof MBeanRegistration) - postRegisterInvoke((MBeanRegistration) mbean, true, false); - - /** - * Checks if the newly registered MBean is a ClassLoader - * If so, tell the ClassLoaderRepository (CLR) about it. We do - * this even if the object is a PrivateClassLoader. In that - * case, the CLR remembers the loader for use when it is - * explicitly named (e.g. as the loader in createMBean) but - * does not add it to the list that is consulted by - * ClassLoaderRepository.loadClass. - */ - final Object resource = getResource(mbean); - if (resource instanceof ClassLoader) { - final ModifiableClassLoaderRepository clr = - instantiator.getClassLoaderRepository(); - if (clr == null) { + if (logicalName == null) { final RuntimeException wrapped = - new IllegalArgumentException( - "Dynamic addition of class loaders is not supported"); + new IllegalArgumentException("No object name specified"); throw new RuntimeOperationsException(wrapped, - "Exception occurred trying to register the MBean as a class loader"); + "Exception occurred trying to register the MBean"); } - clr.addClassLoader(logicalName, (ClassLoader) resource); + + final Object resource = getResource(mbean); + + // Register the MBean with the repository. + // Returns the resource context that was used. + // The returned context does nothing for regular MBeans. + // For ClassLoader MBeans and JMXNamespace (and JMXDomain) + // MBeans - the context makes it possible to register these + // objects with the appropriate framework artifacts, such as + // the CLR or the dispatcher, from within the repository lock. + // In case of success, we also need to call context.done() at the + // end of this method. + // + context = registerWithRepository(resource, mbean, logicalName); + + registerFailed = false; + registered = true; + } finally { + postRegister(mbean, registered, registerFailed); } - return result; + context.done(); + return new ObjectInstance(logicalName, classname); } - private static ObjectName preRegisterInvoke(MBeanRegistration moi, - ObjectName name, - MBeanServer mbs) - throws InstanceAlreadyExistsException, MBeanRegistrationException { - - final ObjectName newName; - + private static void throwMBeanRegistrationException(Throwable t, String where) + throws MBeanRegistrationException { try { - newName = moi.preRegister(mbs, name); + throw t; } catch (RuntimeException e) { - throw new RuntimeMBeanException(e, - "RuntimeException thrown in preRegister method"); + throw new RuntimeMBeanException( + e, "RuntimeException thrown " + where); } catch (Error er) { - throw new RuntimeErrorException(er, - "Error thrown in preRegister method"); + throw new RuntimeErrorException(er, "Error thrown " + where); } catch (MBeanRegistrationException r) { throw r; } catch (Exception ex) { - throw new MBeanRegistrationException(ex, - "Exception thrown in preRegister method"); + throw new MBeanRegistrationException(ex, "Exception thrown " + where); + } catch (Throwable t1) { + throw new RuntimeException(t); // neither Error nor Exception?? + } + } + + private static ObjectName preRegister( + DynamicMBean mbean, MBeanServer mbs, ObjectName name) + throws InstanceAlreadyExistsException, MBeanRegistrationException { + + ObjectName newName = null; + + try { + if (mbean instanceof MBeanRegistration) + newName = ((MBeanRegistration) mbean).preRegister(mbs, name); + } catch (Throwable t) { + throwMBeanRegistrationException(t, "in preRegister method"); } if (newName != null) return newName; else return name; } - private static void postRegisterInvoke(MBeanRegistration moi, - boolean registrationDone, - boolean registerFailed) { - - if (registerFailed && moi instanceof DynamicMBean2) - ((DynamicMBean2) moi).registerFailed(); + private static DynamicMBean injectResources( + DynamicMBean mbean, MBeanServer mbs, ObjectName name) + throws MBeanRegistrationException { try { - moi.postRegister(registrationDone); + Object resource = getResource(mbean); + MBeanInjector.inject(resource, mbs, name); + if (MBeanInjector.injectsSendNotification(resource)) { + NotificationBroadcasterSupport nbs = + new NotificationBroadcasterSupport(); + MBeanInjector.injectSendNotification(resource, nbs); + mbean = NotifySupport.wrap(mbean, nbs); + } + return mbean; + } catch (Throwable t) { + throwMBeanRegistrationException(t, "injecting @Resources"); + return null; // not reached + } + } + + private static void postRegister( + DynamicMBean mbean, boolean registrationDone, boolean registerFailed) { + + if (registerFailed && mbean instanceof DynamicMBean2) + ((DynamicMBean2) mbean).registerFailed(); + try { + if (mbean instanceof MBeanRegistration) + ((MBeanRegistration) mbean).postRegister(registrationDone); } catch (RuntimeException e) { throw new RuntimeMBeanException(e, "RuntimeException thrown in postRegister method"); @@ -1053,17 +1071,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { throws MBeanRegistrationException { try { moi.preDeregister(); - } catch (RuntimeException e) { - throw new RuntimeMBeanException(e, - "RuntimeException thrown in preDeregister method"); - } catch (Error er) { - throw new RuntimeErrorException(er, - "Error thrown in preDeregister method"); - } catch (MBeanRegistrationException t) { - throw t; - } catch (Exception ex) { - throw new MBeanRegistrationException(ex, - "Exception thrown in preDeregister method"); + } catch (Throwable t) { + throwMBeanRegistrationException(t, "in preDeregister method"); } } @@ -1104,12 +1113,19 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } private static Object getResource(DynamicMBean mbean) { - if (mbean instanceof DynamicMBean2) - return ((DynamicMBean2) mbean).getResource(); + if (mbean instanceof DynamicWrapperMBean) + return ((DynamicWrapperMBean) mbean).getWrappedObject(); else return mbean; } + private static ClassLoader getResourceLoader(DynamicMBean mbean) { + if (mbean instanceof DynamicWrapperMBean) + return ((DynamicWrapperMBean) mbean).getWrappedClassLoader(); + else + return mbean.getClass().getClassLoader(); + } + private ObjectName nonDefaultDomain(ObjectName name) { if (name == null || name.getDomain().length() > 0) return name; @@ -1123,14 +1139,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { if one is supplied where it shouldn't be). */ final String completeName = domain + name; - try { - return new ObjectName(completeName); - } catch (MalformedObjectNameException e) { - final String msg = - "Unexpected default domain problem: " + completeName + ": " + - e; - throw EnvHelp.initCause(new IllegalArgumentException(msg), e); - } + return Util.newObjectName(completeName); } public String getDefaultDomain() { @@ -1211,7 +1220,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } NotificationListener listenerWrapper = - getListenerWrapper(listener, name, broadcaster, true); + getListenerWrapper(listener, name, instance, true); broadcaster.addNotificationListener(listenerWrapper, filter, handback); } @@ -1335,7 +1344,6 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { DynamicMBean instance = getMBean(name); checkMBeanPermission(instance, null, name, "removeNotificationListener"); - Object resource = getResource(instance); /* We could simplify the code by assigning broadcaster after assigning listenerWrapper, but that would change the error @@ -1348,7 +1356,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { getNotificationBroadcaster(name, instance, reqClass); NotificationListener listenerWrapper = - getListenerWrapper(listener, name, resource, false); + getListenerWrapper(listener, name, instance, false); if (listenerWrapper == null) throw new ListenerNotFoundException("Unknown listener"); @@ -1366,8 +1374,10 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { private static T getNotificationBroadcaster(ObjectName name, Object instance, Class reqClass) { - if (instance instanceof DynamicMBean2) - instance = ((DynamicMBean2) instance).getResource(); + if (reqClass.isInstance(instance)) + return reqClass.cast(instance); + if (instance instanceof DynamicWrapperMBean) + instance = ((DynamicWrapperMBean) instance).getWrappedObject(); if (reqClass.isInstance(instance)) return reqClass.cast(instance); final RuntimeException exc = @@ -1415,24 +1425,31 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { checkMBeanPermission(instance, null, name, "isInstanceOf"); try { - if (instance instanceof DynamicMBean2) { - Object resource = ((DynamicMBean2) instance).getResource(); - ClassLoader loader = resource.getClass().getClassLoader(); - Class c = Class.forName(className, false, loader); - return c.isInstance(resource); - } + Object resource = getResource(instance); - final String cn = getClassName(instance); - if (cn.equals(className)) + final String resourceClassName = + (resource instanceof DynamicMBean) ? + getClassName((DynamicMBean) resource) : + resource.getClass().getName(); + + if (resourceClassName.equals(className)) return true; - final ClassLoader cl = instance.getClass().getClassLoader(); + final ClassLoader cl = getResourceLoader(instance); final Class classNameClass = Class.forName(className, false, cl); - if (classNameClass.isInstance(instance)) + if (classNameClass.isInstance(resource)) return true; - final Class instanceClass = Class.forName(cn, false, cl); - return classNameClass.isAssignableFrom(instanceClass); + // Ensure that isInstanceOf(NotificationEmitter) is true when + // the MBean is a NotificationEmitter by virtue of a @Resource + // annotation specifying a SendNotification resource. + // This is a hack. + if (instance instanceof NotificationBroadcaster && + classNameClass.isAssignableFrom(NotificationEmitter.class)) + return true; + + final Class resourceClass = Class.forName(resourceClassName, false, cl); + return classNameClass.isAssignableFrom(resourceClass); } catch (Exception x) { /* Could be SecurityException or ClassNotFoundException */ if (MBEANSERVER_LOGGER.isLoggable(Level.FINEST)) { @@ -1457,7 +1474,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { DynamicMBean instance = getMBean(mbeanName); checkMBeanPermission(instance, null, mbeanName, "getClassLoaderFor"); - return getResource(instance).getClass().getClassLoader(); + return getResourceLoader(instance); } /** @@ -1488,40 +1505,6 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { return (ClassLoader) resource; } - /** - * Adds a MBean in the repository - */ - private void internal_addObject(DynamicMBean object, ObjectName logicalName) - throws InstanceAlreadyExistsException { - - // ------------------------------ - // ------------------------------ - - // Let the repository do the work. - - try { - repository.addMBean(object, logicalName); - } catch (InstanceAlreadyExistsException e) { - if (object instanceof MBeanRegistration) { - postRegisterInvoke((MBeanRegistration) object, false, true); - } - throw e; - } - - // --------------------- - // Send create event - // --------------------- - if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { - MBEANSERVER_LOGGER.logp(Level.FINER, - DefaultMBeanServerInterceptor.class.getName(), - "addObject", "Send create notification of object " + - logicalName.getCanonicalName()); - } - - sendNotification(MBeanServerNotification.REGISTRATION_NOTIFICATION, - logicalName ) ; - } - /** * Sends an MBeanServerNotifications with the specified type for the * MBean with the specified ObjectName @@ -1712,9 +1695,10 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { */ private NotificationListener getListenerWrapper(NotificationListener l, ObjectName name, - Object mbean, + DynamicMBean mbean, boolean create) { - ListenerWrapper wrapper = new ListenerWrapper(l, name, mbean); + Object resource = getResource(mbean); + ListenerWrapper wrapper = new ListenerWrapper(l, name, resource); synchronized (listenerWrappers) { WeakReference ref = listenerWrappers.get(wrapper); if (ref != null) { @@ -1758,6 +1742,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { listener.handleNotification(notification, handback); } + @Override public boolean equals(Object o) { if (!(o instanceof ListenerWrapper)) return false; @@ -1774,6 +1759,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { */ } + @Override public int hashCode() { return (System.identityHashCode(listener) ^ System.identityHashCode(mbean)); @@ -1851,4 +1837,213 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } } + // ------------------------------------------------------------------ + // + // Dealing with registration of special MBeans in the repository. + // + // ------------------------------------------------------------------ + + /** + * A RegistrationContext that makes it possible to perform additional + * post registration actions (or post unregistration actions) outside + * of the repository lock, once postRegister (or postDeregister) has + * been called. + * The method {@code done()} will be called in registerMBean or + * unregisterMBean, at the end. + */ + private static interface ResourceContext extends RegistrationContext { + public void done(); + /** An empty ResourceContext which does nothing **/ + public static final ResourceContext NONE = new ResourceContext() { + public void done() {} + public void registering() {} + public void unregistered() {} + }; + } + + /** + * Adds a MBean in the repository, + * sends MBeanServerNotification.REGISTRATION_NOTIFICATION, + * returns ResourceContext for special resources such as ClassLoaders + * or JMXNamespaces. For regular MBean this method returns + * ResourceContext.NONE. + * @return a ResourceContext for special resources such as ClassLoaders + * or JMXNamespaces. + */ + private ResourceContext registerWithRepository( + final Object resource, + final DynamicMBean object, + final ObjectName logicalName) + throws InstanceAlreadyExistsException, + MBeanRegistrationException { + + // Creates a registration context, if needed. + // + final ResourceContext context = + makeResourceContextFor(resource, logicalName); + + + repository.addMBean(object, logicalName, context); + // May throw InstanceAlreadyExistsException + + // --------------------- + // Send create event + // --------------------- + if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { + MBEANSERVER_LOGGER.logp(Level.FINER, + DefaultMBeanServerInterceptor.class.getName(), + "addObject", "Send create notification of object " + + logicalName.getCanonicalName()); + } + + sendNotification( + MBeanServerNotification.REGISTRATION_NOTIFICATION, + logicalName); + + return context; + } + + /** + * Removes a MBean in the repository, + * sends MBeanServerNotification.UNREGISTRATION_NOTIFICATION, + * returns ResourceContext for special resources such as ClassLoaders + * or JMXNamespaces, or null. For regular MBean this method returns + * ResourceContext.NONE. + * + * @return a ResourceContext for special resources such as ClassLoaders + * or JMXNamespaces. + */ + private ResourceContext unregisterFromRepository( + final Object resource, + final DynamicMBean object, + final ObjectName logicalName) + throws InstanceNotFoundException { + + // Creates a registration context, if needed. + // + final ResourceContext context = + makeResourceContextFor(resource, logicalName); + + + repository.remove(logicalName, context); + + // --------------------- + // Send deletion event + // --------------------- + if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { + MBEANSERVER_LOGGER.logp(Level.FINER, + DefaultMBeanServerInterceptor.class.getName(), + "unregisterMBean", "Send delete notification of object " + + logicalName.getCanonicalName()); + } + + sendNotification(MBeanServerNotification.UNREGISTRATION_NOTIFICATION, + logicalName); + return context; + } + + /** + * Registers a ClassLoader with the CLR. + * This method is called by the ResourceContext from within the + * repository lock. + * @param loader The ClassLoader. + * @param logicalName The ClassLoader MBean ObjectName. + */ + private void addClassLoader(ClassLoader loader, + final ObjectName logicalName) { + /** + * Called when the newly registered MBean is a ClassLoader + * If so, tell the ClassLoaderRepository (CLR) about it. We do + * this even if the loader is a PrivateClassLoader. In that + * case, the CLR remembers the loader for use when it is + * explicitly named (e.g. as the loader in createMBean) but + * does not add it to the list that is consulted by + * ClassLoaderRepository.loadClass. + */ + final ModifiableClassLoaderRepository clr = + instantiator.getClassLoaderRepository(); + if (clr == null) { + final RuntimeException wrapped = + new IllegalArgumentException( + "Dynamic addition of class loaders" + + " is not supported"); + throw new RuntimeOperationsException(wrapped, + "Exception occurred trying to register" + + " the MBean as a class loader"); + } + clr.addClassLoader(logicalName, loader); + } + + /** + * Unregisters a ClassLoader from the CLR. + * This method is called by the ResourceContext from within the + * repository lock. + * @param loader The ClassLoader. + * @param logicalName The ClassLoader MBean ObjectName. + */ + private void removeClassLoader(ClassLoader loader, + final ObjectName logicalName) { + /** + * Removes the MBean from the default loader repository. + */ + if (loader != server.getClass().getClassLoader()) { + final ModifiableClassLoaderRepository clr = + instantiator.getClassLoaderRepository(); + if (clr != null) { + clr.removeClassLoader(logicalName); + } + } + } + + /** + * Creates a ResourceContext for a ClassLoader MBean. + * The resource context makes it possible to add the ClassLoader to + * (ResourceContext.registering) or resp. remove the ClassLoader from + * (ResourceContext.unregistered) the CLR + * when the associated MBean is added to or resp. removed from the + * repository. + * + * @param loader The ClassLoader MBean being registered or + * unregistered. + * @param logicalName The name of the ClassLoader MBean. + * @return a ResourceContext that takes in charge the addition or removal + * of the loader to or from the CLR. + */ + private ResourceContext createClassLoaderContext( + final ClassLoader loader, + final ObjectName logicalName) { + return new ResourceContext() { + + public void registering() { + addClassLoader(loader, logicalName); + } + + public void unregistered() { + removeClassLoader(loader, logicalName); + } + + public void done() { + } + }; + } + + /** + * Creates a ResourceContext for the given resource. + * If the resource does not need a ResourceContext, returns + * ResourceContext.NONE. + * At this time, only JMXNamespaces and ClassLoaders need a + * ResourceContext. + * + * @param resource The resource being registered or unregistered. + * @param logicalName The name of the associated MBean. + * @return + */ + private ResourceContext makeResourceContextFor(Object resource, + ObjectName logicalName) { + if (resource instanceof ClassLoader) { + return createClassLoaderContext((ClassLoader) resource, + logicalName); + } + return ResourceContext.NONE; + } } diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/DynamicMBean2.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/DynamicMBean2.java index 49d49ce4c1f..270f7ad77a6 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/DynamicMBean2.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/DynamicMBean2.java @@ -25,7 +25,7 @@ package com.sun.jmx.mbeanserver; -import javax.management.DynamicMBean; +import javax.management.DynamicWrapperMBean; import javax.management.MBeanServer; import javax.management.ObjectName; @@ -35,17 +35,7 @@ import javax.management.ObjectName; * * @since 1.6 */ -public interface DynamicMBean2 extends DynamicMBean { - /** - * The resource corresponding to this MBean. This is the object whose - * class name should be reflected by the MBean's - * getMBeanInfo().getClassName() for example. For a "plain" - * DynamicMBean it will be "this". For an MBean that wraps another - * object, like javax.management.StandardMBean, it will be the wrapped - * object. - */ - public Object getResource(); - +public interface DynamicMBean2 extends DynamicWrapperMBean { /** * The name of this MBean's class, as used by permission checks. * This is typically equal to getResource().getClass().getName(). diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Introspector.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Introspector.java index 10826cc36f1..fb5d19125a6 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Introspector.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Introspector.java @@ -25,23 +25,39 @@ package com.sun.jmx.mbeanserver; +import com.sun.jmx.remote.util.EnvHelp; +import java.beans.BeanInfo; +import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Array; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import javax.management.AttributeNotFoundException; +import javax.management.Description; import javax.management.Descriptor; +import javax.management.DescriptorFields; import javax.management.DescriptorKey; import javax.management.DynamicMBean; import javax.management.ImmutableDescriptor; +import javax.management.MBean; import javax.management.MBeanInfo; +import javax.management.MXBean; import javax.management.NotCompliantMBeanException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.MXBeanMappingFactory; +import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; +import com.sun.jmx.mbeanserver.Util; import com.sun.jmx.remote.util.EnvHelp; import java.beans.BeanInfo; import java.beans.PropertyDescriptor; @@ -133,8 +149,12 @@ public class Introspector { } } - public static void checkCompliance(Class mbeanClass) - throws NotCompliantMBeanException { + public static void checkCompliance(Class mbeanClass) + throws NotCompliantMBeanException { + + // Check that @Resource is used correctly (if it used). + MBeanInjector.validate(mbeanClass); + // Is DynamicMBean? // if (DynamicMBean.class.isAssignableFrom(mbeanClass)) @@ -157,21 +177,39 @@ public class Introspector { } catch (NotCompliantMBeanException e) { mxbeanException = e; } + // Is @MBean or @MXBean class? + // In fact we find @MBean or @MXBean as a hacky variant of + // getStandardMBeanInterface or getMXBeanInterface. If we get here + // then nothing worked. final String msg = "MBean class " + mbeanClass.getName() + " does not implement " + - "DynamicMBean, neither follows the Standard MBean conventions (" + - mbeanException.toString() + ") nor the MXBean conventions (" + - mxbeanException.toString() + ")"; + "DynamicMBean; does not follow the Standard MBean conventions (" + + mbeanException.toString() + "); does not follow the MXBean conventions (" + + mxbeanException.toString() + "); and does not have or inherit the @" + + MBean.class.getSimpleName() + " or @" + MXBean.class.getSimpleName() + + " annotation"; throw new NotCompliantMBeanException(msg); } + /** + *

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}.

+ * @param mbean the object to convert to a DynamicMBean. + * @param 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 DynamicMBean makeDynamicMBean(T mbean) throws NotCompliantMBeanException { if (mbean == null) throw new NotCompliantMBeanException("Null MBean object"); if (mbean instanceof DynamicMBean) return (DynamicMBean) mbean; - final Class mbeanClass = mbean.getClass(); + final Class mbeanClass = mbean.getClass(); Class c = null; try { c = Util.cast(getStandardMBeanInterface(mbeanClass)); @@ -270,7 +308,7 @@ public class Introspector { * Return null if the MBean is a DynamicMBean, * or if no MBean interface is found. */ - public static Class getMBeanInterface(Class baseClass) { + public static Class getMBeanInterface(Class baseClass) { // Check if the given class implements the MBean interface // or the Dynamic MBean interface if (isDynamic(baseClass)) return null; @@ -291,10 +329,12 @@ public class Introspector { * @throws NotCompliantMBeanException The specified class is * not a JMX compliant Standard MBean. */ - public static Class getStandardMBeanInterface(Class baseClass) - throws NotCompliantMBeanException { - Class current = baseClass; - Class mbeanInterface = null; + public static Class getStandardMBeanInterface(Class baseClass) + throws NotCompliantMBeanException { + if (baseClass.isAnnotationPresent(MBean.class)) + return baseClass; + Class current = baseClass; + Class mbeanInterface = null; while (current != null) { mbeanInterface = findMBeanInterface(current, current.getName()); @@ -321,8 +361,10 @@ public class Introspector { * @throws NotCompliantMBeanException The specified class is * not a JMX compliant MXBean. */ - public static Class getMXBeanInterface(Class baseClass) + public static Class getMXBeanInterface(Class baseClass) throws NotCompliantMBeanException { + if (hasMXBeanAnnotation(baseClass)) + return baseClass; try { return MXBeanSupport.findMXBeanInterface(baseClass); } catch (Exception e) { @@ -345,19 +387,24 @@ public class Introspector { * ------------------------------------------ */ + static boolean hasMXBeanAnnotation(Class c) { + MXBean m = c.getAnnotation(MXBean.class); + return (m != null && m.value()); + } /** * Try to find the MBean interface corresponding to the class aName * - i.e. aNameMBean, from within aClass and its superclasses. **/ - private static Class findMBeanInterface(Class aClass, String aName) { - Class current = aClass; + private static Class findMBeanInterface( + Class aClass, String aName) { + Class current = aClass; while (current != null) { - final Class[] interfaces = current.getInterfaces(); + final Class[] interfaces = current.getInterfaces(); final int len = interfaces.length; for (int i=0;i inter = Util.cast(interfaces[i]); + inter = implementsMBean(inter, aName); if (inter != null) return inter; } current = current.getSuperclass(); @@ -365,6 +412,48 @@ public class Introspector { return null; } + public static String descriptionForElement(AnnotatedElement elmt) { + if (elmt == null) + return null; + Description d = elmt.getAnnotation(Description.class); + if (d == null) + return null; + return d.value(); + } + + public static String descriptionForParameter( + Annotation[] parameterAnnotations) { + for (Annotation a : parameterAnnotations) { + if (a instanceof Description) + return ((Description) a).value(); + } + return null; + } + + public static String nameForParameter( + Annotation[] parameterAnnotations) { + for (Annotation a : parameterAnnotations) { + Class ac = a.annotationType(); + // You'd really have to go out of your way to have more than + // one @Name annotation, so we don't check for that. + if (ac.getSimpleName().equals("Name")) { + try { + Method value = ac.getMethod("value"); + if (value.getReturnType() == String.class && + value.getParameterTypes().length == 0) { + return (String) value.invoke(a); + } + } catch (Exception e) { + MBEANSERVER_LOGGER.log( + Level.WARNING, + "Unexpected exception getting @" + ac.getName(), + e); + } + } + } + return null; + } + public static Descriptor descriptorForElement(final AnnotatedElement elmt) { if (elmt == null) return ImmutableDescriptor.EMPTY_DESCRIPTOR; @@ -372,41 +461,18 @@ public class Introspector { return descriptorForAnnotations(annots); } + public static Descriptor descriptorForAnnotation(Annotation annot) { + return descriptorForAnnotations(new Annotation[] {annot}); + } + public static Descriptor descriptorForAnnotations(Annotation[] annots) { if (annots.length == 0) return ImmutableDescriptor.EMPTY_DESCRIPTOR; Map descriptorMap = new HashMap(); for (Annotation a : annots) { - Class c = a.annotationType(); - Method[] elements = c.getMethods(); - for (Method element : elements) { - DescriptorKey key = element.getAnnotation(DescriptorKey.class); - if (key != null) { - String name = key.value(); - Object value; - try { - value = element.invoke(a); - } catch (RuntimeException e) { - // we don't expect this - except for possibly - // security exceptions? - // RuntimeExceptions shouldn't be "UndeclaredThrowable". - // anyway... - // - throw e; - } catch (Exception e) { - // we don't expect this - throw new UndeclaredThrowableException(e); - } - value = annotationToField(value); - Object oldValue = descriptorMap.put(name, value); - if (oldValue != null && !equals(oldValue, value)) { - final String msg = - "Inconsistent values for descriptor field " + name + - " from annotations: " + value + " :: " + oldValue; - throw new IllegalArgumentException(msg); - } - } - } + if (a instanceof DescriptorFields) + addDescriptorFieldsToMap(descriptorMap, (DescriptorFields) a); + addAnnotationFieldsToMap(descriptorMap, a); } if (descriptorMap.isEmpty()) @@ -415,6 +481,62 @@ public class Introspector { return new ImmutableDescriptor(descriptorMap); } + private static void addDescriptorFieldsToMap( + Map 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 descriptorMap, Annotation a) { + Class 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 descriptorMap, String name, Object value) { + Object oldValue = descriptorMap.put(name, value); + if (oldValue != null && !equals(oldValue, value)) { + final String msg = + "Inconsistent values for descriptor field " + name + + " from annotations: " + value + " :: " + oldValue; + throw new IllegalArgumentException(msg); + } + } + /** * Throws a NotCompliantMBeanException or a SecurityException. * @param notCompliant the class which was under examination @@ -473,8 +595,13 @@ public class Introspector { // The only other possibility is that the value is another // annotation, or that the language has evolved since this code // was written. We don't allow for either of those currently. + // If it is indeed another annotation, then x will be a proxy + // with an unhelpful name like $Proxy2. So we extract the + // proxy's interface to use that in the exception message. + if (Proxy.isProxyClass(c)) + c = c.getInterfaces()[0]; // array "can't be empty" throw new IllegalArgumentException("Illegal type for annotation " + - "element: " + x.getClass().getName()); + "element using @DescriptorKey: " + c.getName()); } // This must be consistent with the check for duplicate field values in @@ -490,15 +617,15 @@ public class Introspector { * @param c The interface to be tested * @param clName The name of the class implementing this interface */ - private static Class implementsMBean(Class c, String clName) { + private static Class implementsMBean(Class c, String clName) { String clMBeanName = clName + "MBean"; if (c.getName().equals(clMBeanName)) { return c; } - Class[] interfaces = c.getInterfaces(); + Class[] interfaces = c.getInterfaces(); for (int i = 0;i < interfaces.length; i++) { if (interfaces[i].getName().equals(clMBeanName)) - return interfaces[i]; + return Util.cast(interfaces[i]); } return null; diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanAnalyzer.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanAnalyzer.java index 58328e1ce8a..0661a94a404 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanAnalyzer.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanAnalyzer.java @@ -33,6 +33,10 @@ import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; +import javax.management.MBean; +import javax.management.MXBean; +import javax.management.ManagedAttribute; +import javax.management.ManagedOperation; import javax.management.NotCompliantMBeanException; /** @@ -125,18 +129,26 @@ class MBeanAnalyzer { for (Method m : methods) { final String name = m.getName(); final int nParams = m.getParameterTypes().length; + final boolean managedOp = m.isAnnotationPresent(ManagedOperation.class); + final boolean managedAttr = m.isAnnotationPresent(ManagedAttribute.class); + if (managedOp && managedAttr) { + throw new NotCompliantMBeanException("Method " + name + + " has both @ManagedOperation and @ManagedAttribute"); + } final M cm = introspector.mFrom(m); String attrName = ""; - if (name.startsWith("get")) - attrName = name.substring(3); - else if (name.startsWith("is") - && m.getReturnType() == boolean.class) - attrName = name.substring(2); + if (!managedOp) { + if (name.startsWith("get")) + attrName = name.substring(3); + else if (name.startsWith("is") + && m.getReturnType() == boolean.class) + attrName = name.substring(2); + } if (attrName.length() != 0 && nParams == 0 - && m.getReturnType() != void.class) { + && m.getReturnType() != void.class && !managedOp) { // It's a getter // Check we don't have both isX and getX AttrMethods am = attrMap.get(attrName); @@ -153,7 +165,7 @@ class MBeanAnalyzer { attrMap.put(attrName, am); } else if (name.startsWith("set") && name.length() > 3 && nParams == 1 && - m.getReturnType() == void.class) { + m.getReturnType() == void.class && !managedOp) { // It's a setter attrName = name.substring(3); AttrMethods am = attrMap.get(attrName); @@ -166,6 +178,9 @@ class MBeanAnalyzer { } am.setter = cm; attrMap.put(attrName, am); + } else if (managedAttr) { + throw new NotCompliantMBeanException("Method " + name + + " has @ManagedAttribute but is not a valid getter or setter"); } else { // It's an operation List cms = opMap.get(name); diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java new file mode 100644 index 00000000000..4831134f6af --- /dev/null +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java @@ -0,0 +1,291 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.mbeanserver; + +import java.lang.ref.WeakReference; +import java.security.PrivilegedAction; +import java.util.Map; +import java.util.WeakHashMap; +import javax.annotation.Resource; +import javax.management.MBeanServer; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; + +import static com.sun.jmx.mbeanserver.Util.newMap; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.management.SendNotification; + +public class MBeanInjector { + private static Class[] injectedClasses = { + MBeanServer.class, ObjectName.class, SendNotification.class, + }; + + public static void inject(Object mbean, MBeanServer mbs, ObjectName name) + throws Exception { + ClassInjector injector = injectorForClass(mbean.getClass()); + injector.inject(mbean, MBeanServer.class, mbs); + injector.inject(mbean, ObjectName.class, name); + } + + public static boolean injectsSendNotification(Object mbean) + throws NotCompliantMBeanException { + ClassInjector injector = injectorForClass(mbean.getClass()); + return injector.injects(SendNotification.class); + } + + public static void injectSendNotification(Object mbean, SendNotification sn) + throws Exception { + ClassInjector injector = injectorForClass(mbean.getClass()); + injector.inject(mbean, SendNotification.class, sn); + } + + public static void validate(Class c) throws NotCompliantMBeanException { + injectorForClass(c); + } + + private static class ClassInjector { + private Map, List> fields; + private Map, List> 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); + } + + void inject(Object instance, Class type, T resource) + throws Exception { + List fs = fields.get(type); + if (fs != null) { + for (Field f : fs) + f.set(instance, resource); + } + List 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, List> entry : methods.entrySet()) { + List 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() { + 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() { + public Void run() { + member.setAccessible(true); + return null; + } + }); + addToMap(fields, injectedClass, field); + addToMap(methods, injectedClass, method); + } + } + } + } + } + + private static void addToMap(Map> map, K key, V value) { + if (value == null) + return; + List list = map.get(key); + if (list == null) + list = Collections.singletonList(value); + else { + if (list.size() == 1) + list = new ArrayList(list); + list.add(value); + } + map.put(key, list); + } + } + + private static synchronized ClassInjector injectorForClass(Class c) + throws NotCompliantMBeanException { + WeakReference wr = injectorMap.get(c); + ClassInjector ci = (wr == null) ? null : wr.get(); + if (ci == null) { + ci = new ClassInjector(c); + injectorMap.put(c, new WeakReference(ci)); + } + return ci; + } + + private static Map, WeakReference> injectorMap = + new WeakHashMap, WeakReference>(); +} diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java index ab14aad19f4..55b7a6655e8 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java @@ -36,20 +36,28 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.WeakHashMap; +import javax.management.Description; import javax.management.Descriptor; import javax.management.ImmutableDescriptor; import javax.management.IntrospectionException; import javax.management.InvalidAttributeValueException; +import javax.management.MBean; import javax.management.MBeanAttributeInfo; import javax.management.MBeanConstructorInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; +import javax.management.MXBean; +import javax.management.ManagedAttribute; +import javax.management.ManagedOperation; import javax.management.NotCompliantMBeanException; import javax.management.NotificationBroadcaster; +import javax.management.NotificationInfo; +import javax.management.NotificationInfos; import javax.management.ReflectionException; /** @@ -153,6 +161,25 @@ abstract class MBeanIntrospector { abstract MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, M getter, M setter) throws IntrospectionException; + final String getAttributeDescription( + String attributeName, String defaultDescription, + Method getter, Method setter) throws IntrospectionException { + String g = Introspector.descriptionForElement(getter); + String s = Introspector.descriptionForElement(setter); + if (g == null) { + if (s == null) + return defaultDescription; + else + return s; + } else if (s == null || g.equals(s)) { + return g; + } else { + throw new IntrospectionException( + "Inconsistent @Description on getter and setter for " + + "attribute " + attributeName); + } + } + /** * Construct an MBeanOperationInfo for the given operation based on * the M it was derived from. @@ -184,8 +211,12 @@ abstract class MBeanIntrospector { } void checkCompliance(Class mbeanType) throws NotCompliantMBeanException { - if (!mbeanType.isInterface()) { - throw new NotCompliantMBeanException("Not an interface: " + + if (!mbeanType.isInterface() && + !mbeanType.isAnnotationPresent(MBean.class) && + !Introspector.hasMXBeanAnnotation(mbeanType)) { + throw new NotCompliantMBeanException("Not an interface and " + + "does not have @" + MBean.class.getSimpleName() + + " or @" + MXBean.class.getSimpleName() + " annotation: " + mbeanType.getName()); } } @@ -194,7 +225,12 @@ abstract class MBeanIntrospector { * Get the methods to be analyzed to build the MBean interface. */ List getMethods(final Class mbeanType) throws Exception { - return Arrays.asList(mbeanType.getMethods()); + if (mbeanType.isInterface()) + return Arrays.asList(mbeanType.getMethods()); + + final List methods = newList(); + getAnnotatedMethods(mbeanType, methods); + return methods; } final PerInterface getPerInterface(Class mbeanInterface) @@ -232,8 +268,11 @@ abstract class MBeanIntrospector { MBeanAnalyzer analyzer) throws IntrospectionException { final MBeanInfoMaker maker = new MBeanInfoMaker(); analyzer.visit(maker); - final String description = + final String defaultDescription = "Information on the management interface of the MBean"; + String description = Introspector.descriptionForElement(mbeanInterface); + if (description == null) + description = defaultDescription; return maker.makeMBeanInfo(mbeanInterface, description); } @@ -407,7 +446,15 @@ abstract class MBeanIntrospector { throws NotCompliantMBeanException { MBeanInfo mbi = getClassMBeanInfo(resource.getClass(), perInterface); - MBeanNotificationInfo[] notifs = findNotifications(resource); + MBeanNotificationInfo[] notifs; + try { + notifs = findNotifications(resource); + } catch (RuntimeException e) { + NotCompliantMBeanException x = + new NotCompliantMBeanException(e.getMessage()); + x.initCause(e); + throw x; + } Descriptor d = getSpecificMBeanDescriptor(); boolean anyNotifs = (notifs != null && notifs.length > 0); if (!anyNotifs && ImmutableDescriptor.EMPTY_DESCRIPTOR.equals(d)) @@ -460,13 +507,43 @@ abstract class MBeanIntrospector { } } + /* + * 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 methods) + throws Exception { + Class sup = c.getSuperclass(); + if (sup != null) + getAnnotatedMethods(sup, methods); + Class[] intfs = c.getInterfaces(); + for (Class intf : intfs) + getAnnotatedMethods(intf, methods); + for (Method m : c.getMethods()) { + // We are careful not to add m if it is inherited from a parent + // class or interface, because duplicate methods lead to nasty + // behaviour in eliminateCovariantMethods. + if (m.getDeclaringClass() == c && + (m.isAnnotationPresent(ManagedAttribute.class) || + m.isAnnotationPresent(ManagedOperation.class))) + methods.add(m); + } + } + static MBeanNotificationInfo[] findNotifications(Object moi) { if (!(moi instanceof NotificationBroadcaster)) return null; MBeanNotificationInfo[] mbn = ((NotificationBroadcaster) moi).getNotificationInfo(); if (mbn == null || mbn.length == 0) - return null; + return findNotificationsFromAnnotations(moi.getClass()); MBeanNotificationInfo[] result = new MBeanNotificationInfo[mbn.length]; for (int i = 0; i < mbn.length; i++) { @@ -478,11 +555,81 @@ abstract class MBeanIntrospector { 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 list = newList(); + if (ni != null) + list.add(ni); + if (nis != null) + list.addAll(Arrays.asList(nis.value())); + if (list.isEmpty()) + return null; + List 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, WeakReference>> + annotatedNotificationInfoClasses = newWeakHashMap(); + + private static Class getAnnotatedNotificationInfoClass(Class baseClass) { + synchronized (annotatedNotificationInfoClasses) { + WeakReference> 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>(c)); + return c; + } + } + private static MBeanConstructorInfo[] findConstructors(Class c) { Constructor[] cons = c.getConstructors(); MBeanConstructorInfo[] mbc = new MBeanConstructorInfo[cons.length]; for (int i = 0; i < cons.length; i++) { - final String descr = "Public constructor of the MBean"; + String descr = "Public constructor of the MBean"; + Description d = cons[i].getAnnotation(Description.class); + if (d != null) + descr = d.value(); mbc[i] = new MBeanConstructorInfo(descr, cons[i]); } return mbc; diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java index d69f6e77161..33df5764998 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java @@ -263,10 +263,14 @@ public abstract class MBeanSupport return resource.getClass().getName(); } - public final Object getResource() { + public final Object getWrappedObject() { return resource; } + public final ClassLoader getWrappedClassLoader() { + return resource.getClass().getClassLoader(); + } + public final Class getMBeanInterface() { return perInterface.getMBeanInterface(); } diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanIntrospector.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanIntrospector.java index e3b037d29d8..622c765b4ae 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanIntrospector.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanIntrospector.java @@ -35,6 +35,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Map; import java.util.WeakHashMap; +import javax.management.Description; import javax.management.Descriptor; import javax.management.ImmutableDescriptor; import javax.management.IntrospectionException; @@ -43,6 +44,7 @@ import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; +import javax.management.ManagedOperation; import javax.management.NotCompliantMBeanException; import javax.management.openmbean.MXBeanMappingFactory; import javax.management.openmbean.OpenMBeanAttributeInfoSupport; @@ -180,7 +182,10 @@ class MXBeanIntrospector extends MBeanIntrospector { final boolean isWritable = (setter != null); final boolean isIs = isReadable && getName(getter).startsWith("is"); - final String description = attributeName; + final String description = getAttributeDescription( + attributeName, attributeName, + getter == null ? null : getter.getMethod(), + setter == null ? null : setter.getMethod()); final OpenType openType; final Type originalType; @@ -229,13 +234,17 @@ class MXBeanIntrospector extends MBeanIntrospector { MBeanOperationInfo getMBeanOperationInfo(String operationName, ConvertingMethod operation) { final Method method = operation.getMethod(); - final String description = operationName; + String description = operationName; /* Ideally this would be an empty string, but - OMBOperationInfo constructor forbids that. Also, we - could consult an annotation to get a useful - description. */ + OMBOperationInfo constructor forbids that. */ + Description d = method.getAnnotation(Description.class); + if (d != null) + description = d.value(); - final int impact = MBeanOperationInfo.UNKNOWN; + int impact = MBeanOperationInfo.UNKNOWN; + ManagedOperation annot = method.getAnnotation(ManagedOperation.class); + if (annot != null) + impact = annot.impact().getCode(); final OpenType returnType = operation.getOpenReturnType(); final Type originalReturnType = operation.getGenericReturnType(); @@ -247,8 +256,15 @@ class MXBeanIntrospector extends MBeanIntrospector { boolean openParameterTypes = true; Annotation[][] annots = method.getParameterAnnotations(); for (int i = 0; i < paramTypes.length; i++) { - final String paramName = "p" + i; - final String paramDescription = paramName; + String paramName = Introspector.nameForParameter(annots[i]); + if (paramName == null) + paramName = "p" + i; + + String paramDescription = + Introspector.descriptionForParameter(annots[i]); + if (paramDescription == null) + paramDescription = paramName; + final OpenType openType = paramTypes[i]; final Type originalType = originalParamTypes[i]; Descriptor descriptor = diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java index cb8587450b8..34295938e32 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java @@ -161,7 +161,7 @@ public class MXBeanSupport extends MBeanSupport { synchronized (lock) { this.mxbeanLookup = MXBeanLookup.Plain.lookupFor(server); - this.mxbeanLookup.addReference(name, getResource()); + this.mxbeanLookup.addReference(name, getWrappedObject()); this.objectName = name; } } @@ -170,7 +170,7 @@ public class MXBeanSupport extends MBeanSupport { public void unregister() { synchronized (lock) { if (mxbeanLookup != null) { - if (mxbeanLookup.removeReference(objectName, getResource())) + if (mxbeanLookup.removeReference(objectName, getWrappedObject())) objectName = null; } // XXX: need to revisit the whole register/unregister logic in diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/NotifySupport.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/NotifySupport.java new file mode 100644 index 00000000000..94227370ba2 --- /dev/null +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/NotifySupport.java @@ -0,0 +1,186 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.mbeanserver; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.DynamicMBean; +import javax.management.DynamicWrapperMBean; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.ReflectionException; + +/** + * Create wrappers for DynamicMBean that implement NotificationEmitter + * and SendNotification. + */ +public class NotifySupport + implements DynamicMBean2, NotificationEmitter, MBeanRegistration { + + private final DynamicMBean mbean; + private final NotificationBroadcasterSupport nbs; + + public static DynamicMBean wrap( + DynamicMBean mbean, NotificationBroadcasterSupport nbs) { + return new NotifySupport(mbean, nbs); + } + + private NotifySupport(DynamicMBean mbean, NotificationBroadcasterSupport nbs) { + this.mbean = mbean; + this.nbs = nbs; + } + + public static NotificationBroadcasterSupport getNB(DynamicMBean mbean) { + if (mbean instanceof NotifySupport) + return ((NotifySupport) mbean).nbs; + else + return null; + } + + public String getClassName() { + if (mbean instanceof DynamicMBean2) + return ((DynamicMBean2) mbean).getClassName(); + Object w = mbean; + if (w instanceof DynamicWrapperMBean) + w = ((DynamicWrapperMBean) w).getWrappedObject(); + return w.getClass().getName(); + } + + public void preRegister2(MBeanServer mbs, ObjectName name) throws Exception { + if (mbean instanceof DynamicMBean2) + ((DynamicMBean2) mbean).preRegister2(mbs, name); + } + + public void registerFailed() { + if (mbean instanceof DynamicMBean2) + ((DynamicMBean2) mbean).registerFailed(); + } + + public Object getWrappedObject() { + if (mbean instanceof DynamicWrapperMBean) + return ((DynamicWrapperMBean) mbean).getWrappedObject(); + else + return mbean; + } + + public ClassLoader getWrappedClassLoader() { + if (mbean instanceof DynamicWrapperMBean) + return ((DynamicWrapperMBean) mbean).getWrappedClassLoader(); + else + return mbean.getClass().getClassLoader(); + } + + public Object getAttribute(String attribute) throws AttributeNotFoundException, + MBeanException, + ReflectionException { + return mbean.getAttribute(attribute); + } + + public void setAttribute(Attribute attribute) throws AttributeNotFoundException, + InvalidAttributeValueException, + MBeanException, + ReflectionException { + mbean.setAttribute(attribute); + } + + public AttributeList setAttributes(AttributeList attributes) { + return mbean.setAttributes(attributes); + } + + public Object invoke(String actionName, Object[] params, String[] signature) + throws MBeanException, ReflectionException { + return mbean.invoke(actionName, params, signature); + } + + public MBeanInfo getMBeanInfo() { + return mbean.getMBeanInfo(); + } + + public AttributeList getAttributes(String[] attributes) { + return mbean.getAttributes(attributes); + } + + public void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) throws ListenerNotFoundException { + nbs.removeNotificationListener(listener, filter, handback); + } + + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + nbs.removeNotificationListener(listener); + } + + public MBeanNotificationInfo[] getNotificationInfo() { + return nbs.getNotificationInfo(); + } + + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) { + nbs.addNotificationListener(listener, filter, handback); + } + + public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { + if (mbr() != null) + return mbr().preRegister(server, name); + else + return name; + } + + public void postRegister(Boolean registrationDone) { + if (mbr() != null) + mbr().postRegister(registrationDone); + } + + public void preDeregister() throws Exception { + if (mbr() != null) + mbr().preDeregister(); + } + + public void postDeregister() { + if (mbr() != null) + mbr().postDeregister(); + } + + private MBeanRegistration mbr() { + if (mbean instanceof MBeanRegistration) + return (MBeanRegistration) mbean; + else + return null; + } +} diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Repository.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Repository.java index 075ce819937..5186fa39984 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Repository.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Repository.java @@ -29,6 +29,7 @@ import com.sun.jmx.defaults.ServiceName; import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -39,7 +40,6 @@ import java.util.Set; import javax.management.DynamicMBean; import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; -import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.QueryExp; import javax.management.RuntimeOperationsException; @@ -52,6 +52,27 @@ import javax.management.RuntimeOperationsException; */ public class Repository { + /** + * An interface that allows the caller to get some control + * over the registration. + * @see #addMBean + * @see #remove + */ + public interface RegistrationContext { + /** + * Called by {@link #addMBean}. + * Can throw a RuntimeOperationsException to cancel the + * registration. + */ + public void registering(); + + /** + * Called by {@link #remove}. + * Any exception thrown by this method will be ignored. + */ + public void unregistered(); + } + // Private fields --------------------------------------------> /** @@ -115,7 +136,6 @@ public class Repository { /** * Builds a new ObjectNamePattern object from an ObjectName pattern * constituents. - * @param domain pattern.getDomain(). * @param propertyListPattern pattern.isPropertyListPattern(). * @param propertyValuePattern pattern.isPropertyValuePattern(). * @param canonicalProps pattern.getCanonicalKeyPropertyListString(). @@ -216,16 +236,6 @@ public class Repository { } } - private void addNewDomMoi(final DynamicMBean object, final String dom, - final ObjectName name) { - final Map moiTb = - new HashMap(); - moiTb.put(name.getCanonicalKeyPropertyListString(), - new NamedObject(name, object)); - domainTb.put(dom, moiTb); - nbElements++; - } - /** Match a string against a shell-style pattern. The only pattern characters recognised are ?, standing for any one character, and *, standing for any string of @@ -306,6 +316,50 @@ public class Repository { } } + private void addNewDomMoi(final DynamicMBean object, + final String dom, + final ObjectName name, + final RegistrationContext context) { + final Map moiTb = + new HashMap(); + 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 moiTb, + final RegistrationContext context) { + registering(context); + moiTb.put(key,new NamedObject(name, object)); + } + /** * Retrieves the named object contained in repository * from the given objectname. @@ -355,12 +409,12 @@ public class Repository { domainTb = new HashMap>(5); if (domain != null && domain.length() != 0) - this.domain = domain; + this.domain = domain.intern(); // we use == domain later on... else this.domain = ServiceName.DOMAIN; - // Creates an new hastable for the default domain - domainTb.put(this.domain.intern(), new HashMap()); + // Creates a new hashtable for the default domain + domainTb.put(this.domain, new HashMap()); } /** @@ -395,10 +449,21 @@ public class Repository { /** * Stores an MBean associated with its object name in the repository. * - * @param object MBean to be stored in the repository. - * @param name MBean object name. + * @param object MBean to be stored in the repository. + * @param name MBean object name. + * @param context A registration context. If non null, the repository + * will call {@link RegistrationContext#registering() + * context.registering()} from within the repository + * lock, when it has determined that the {@code object} + * can be stored in the repository with that {@code name}. + * If {@link RegistrationContext#registering() + * context.registering()} throws an exception, the + * operation is abandonned, the MBean is not added to the + * repository, and a {@link RuntimeOperationsException} + * is thrown. */ - public void addMBean(final DynamicMBean object, ObjectName name) + public void addMBean(final DynamicMBean object, ObjectName name, + final RegistrationContext context) throws InstanceAlreadyExistsException { if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { @@ -431,7 +496,7 @@ public class Repository { lock.writeLock().lock(); try { - // Domain cannot be JMImplementation if entry does not exists + // Domain cannot be JMImplementation if entry does not exist if ( !to_default_domain && dom.equals("JMImplementation") && domainTb.containsKey("JMImplementation")) { @@ -440,21 +505,21 @@ public class Repository { "Repository: domain name cannot be JMImplementation")); } - // If domain not already exists, add it to the hash table + // If domain does not already exist, add it to the hash table final Map moiTb = domainTb.get(dom); if (moiTb == null) { - addNewDomMoi(object, dom, name); + addNewDomMoi(object, dom, name, context); return; - } - - // Add instance if not already present - String cstr = name.getCanonicalKeyPropertyListString(); - NamedObject elmt= moiTb.get(cstr); - if (elmt != null) { - throw new InstanceAlreadyExistsException(name.toString()); } else { - nbElements++; - moiTb.put(cstr, new NamedObject(name, object)); + // Add instance if not already present + String cstr = name.getCanonicalKeyPropertyListString(); + NamedObject elmt= moiTb.get(cstr); + if (elmt != null) { + throw new InstanceAlreadyExistsException(name.toString()); + } else { + nbElements++; + addMoiToTb(object,name,cstr,moiTb,context); + } } } finally { @@ -533,7 +598,7 @@ public class Repository { // ":*", ":[key=value],*" : names in defaultDomain // "domain:*", "domain:[key=value],*" : names in the specified domain - // Surely one of the most frequent case ... query on the whole world + // Surely one of the most frequent cases ... query on the whole world ObjectName name; if (pattern == null || pattern.getCanonicalName().length() == 0 || @@ -546,8 +611,7 @@ public class Repository { // If pattern is not a pattern, retrieve this mbean ! if (!name.isPattern()) { - final NamedObject no; - no = retrieveNamedObject(name); + final NamedObject no = retrieveNamedObject(name); if (no != null) result.add(no); return result; } @@ -577,12 +641,22 @@ public class Repository { return result; } + if (!name.isDomainPattern()) { + final Map moiTb = domainTb.get(name.getDomain()); + if (moiTb == null) return Collections.emptySet(); + if (allNames) + result.addAll(moiTb.values()); + else + addAllMatching(moiTb, result, namePattern); + return result; + } + // Pattern matching in the domain name (*, ?) char[] dom2Match = name.getDomain().toCharArray(); - for (String domain : domainTb.keySet()) { - char[] theDom = domain.toCharArray(); + for (String dom : domainTb.keySet()) { + char[] theDom = dom.toCharArray(); if (wildmatch(theDom, dom2Match)) { - final Map moiTb = domainTb.get(domain); + final Map moiTb = domainTb.get(dom); if (allNames) result.addAll(moiTb.values()); else @@ -599,11 +673,21 @@ public class Repository { * Removes an MBean from the repository. * * @param name name of the MBean to remove. + * @param context A registration context. If non null, the repository + * will call {@link RegistrationContext#unregistered() + * context.unregistered()} from within the repository + * lock, just after the mbean associated with + * {@code name} is removed from the repository. + * If {@link RegistrationContext#unregistered() + * context.unregistered()} is not expected to throw any + * exception. If it does, the exception is logged + * and swallowed. * * @exception InstanceNotFoundException The MBean does not exist in * the repository. */ - public void remove(final ObjectName name) + public void remove(final ObjectName name, + final RegistrationContext context) throws InstanceNotFoundException { // Debugging stuff @@ -645,6 +729,9 @@ public class Repository { if (dom == domain) domainTb.put(domain, new HashMap()); } + + unregistering(context,name); + } finally { lock.writeLock().unlock(); } diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanIntrospector.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanIntrospector.java index 2237a5192cd..aca58c32f11 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanIntrospector.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanIntrospector.java @@ -35,6 +35,7 @@ import javax.management.IntrospectionException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanOperationInfo; +import javax.management.ManagedOperation; import javax.management.NotCompliantMBeanException; import javax.management.NotificationBroadcaster; import javax.management.NotificationBroadcasterSupport; @@ -118,22 +119,32 @@ class StandardMBeanIntrospector extends MBeanIntrospector { @Override MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, - Method getter, Method setter) { + Method getter, Method setter) throws IntrospectionException { - final String description = "Attribute exposed for management"; - try { - return new MBeanAttributeInfo(attributeName, description, - getter, setter); - } catch (IntrospectionException e) { - throw new RuntimeException(e); // should not happen - } + String description = getAttributeDescription( + attributeName, "Attribute exposed for management", + getter, setter); + return new MBeanAttributeInfo(attributeName, description, + getter, setter); } @Override MBeanOperationInfo getMBeanOperationInfo(String operationName, Method operation) { - final String description = "Operation exposed for management"; - return new MBeanOperationInfo(description, operation); + final String defaultDescription = "Operation exposed for management"; + String description = Introspector.descriptionForElement(operation); + if (description == null) + description = defaultDescription; + + int impact = MBeanOperationInfo.UNKNOWN; + ManagedOperation annot = operation.getAnnotation(ManagedOperation.class); + if (annot != null) + impact = annot.impact().getCode(); + + MBeanOperationInfo mboi = new MBeanOperationInfo(description, operation); + return new MBeanOperationInfo( + mboi.getName(), mboi.getDescription(), mboi.getSignature(), + mboi.getReturnType(), impact, mboi.getDescriptor()); } @Override diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java index 131c5341b16..448a5b6be86 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java @@ -41,26 +41,24 @@ import javax.management.openmbean.MXBeanMappingFactory; public class StandardMBeanSupport extends MBeanSupport { /** -

Construct a Standard MBean that wraps the given resource using the - given Standard MBean interface.

- - @param resource the underlying resource for the new MBean. - - @param mbeanInterface the interface to be used to determine - the MBean's management interface. - - @param a type parameter that allows the compiler to check - that {@code resource} implements {@code mbeanInterface}, - provided that {@code mbeanInterface} is a class constant like - {@code SomeMBean.class}. - - @throws IllegalArgumentException if {@code resource} is null or - if it does not implement the class {@code mbeanInterface} or if - that class is not a valid Standard MBean interface. - */ - public StandardMBeanSupport(T resource, Class mbeanInterface) + *

Construct a Standard MBean that wraps the given resource using the + * given Standard MBean interface.

+ * + * @param resource the underlying resource for the new MBean. + * @param mbeanInterfaceType the class or interface to be used to determine + * the MBean's management interface. An interface if this is a + * classic Standard MBean; a class if this is a {@code @ManagedResource}. + * @param a type parameter that allows the compiler to check + * that {@code resource} implements {@code mbeanInterfaceType}, + * provided that {@code mbeanInterfaceType} is a class constant like + * {@code SomeMBean.class}. + * @throws IllegalArgumentException if {@code resource} is null or + * if it does not implement the class {@code mbeanInterfaceType} or if + * that class is not a valid Standard MBean interface. + */ + public StandardMBeanSupport(T resource, Class mbeanInterfaceType) throws NotCompliantMBeanException { - super(resource, mbeanInterface, (MXBeanMappingFactory) null); + super(resource, mbeanInterfaceType, (MXBeanMappingFactory) null); } @Override @@ -86,13 +84,14 @@ public class StandardMBeanSupport extends MBeanSupport { @Override public MBeanInfo getMBeanInfo() { MBeanInfo mbi = super.getMBeanInfo(); - Class resourceClass = getResource().getClass(); - if (StandardMBeanIntrospector.isDefinitelyImmutableInfo(resourceClass)) + Class resourceClass = getWrappedObject().getClass(); + if (!getMBeanInterface().isInterface() || + StandardMBeanIntrospector.isDefinitelyImmutableInfo(resourceClass)) return mbi; return new MBeanInfo(mbi.getClassName(), mbi.getDescription(), mbi.getAttributes(), mbi.getConstructors(), mbi.getOperations(), - MBeanIntrospector.findNotifications(getResource()), + MBeanIntrospector.findNotifications(getWrappedObject()), mbi.getDescriptor()); } } diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Util.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Util.java index f2dbe60bbdf..b7d31ed0c9e 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Util.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Util.java @@ -38,6 +38,7 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import java.util.WeakHashMap; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; @@ -71,6 +72,10 @@ public class Util { return new LinkedHashMap(); } + static WeakHashMap newWeakHashMap() { + return new WeakHashMap(); + } + static Set newSet() { return new HashSet(); } diff --git a/jdk/src/share/classes/javax/management/BinaryRelQueryExp.java b/jdk/src/share/classes/javax/management/BinaryRelQueryExp.java index a624850c5d5..7874b8e2d6c 100644 --- a/jdk/src/share/classes/javax/management/BinaryRelQueryExp.java +++ b/jdk/src/share/classes/javax/management/BinaryRelQueryExp.java @@ -192,6 +192,7 @@ class BinaryRelQueryExp extends QueryEval implements QueryExp { return "(" + exp1 + ") " + relOpString() + " (" + exp2 + ")"; } + @Override String toQueryString() { return exp1 + " " + relOpString() + " " + exp2; } diff --git a/jdk/src/share/classes/javax/management/Description.java b/jdk/src/share/classes/javax/management/Description.java new file mode 100644 index 00000000000..a0bf96d9b1d --- /dev/null +++ b/jdk/src/share/classes/javax/management/Description.java @@ -0,0 +1,180 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.ResourceBundle; + +/** + *

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.

+ * + *

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:

+ * + *
+ * {@code @Description}("Application configuration")
+ * public interface ConfigurationMBean {
+ *     {@code @Description}("Cache size in bytes")
+ *     public int getCacheSize();
+ *     public void setCacheSize(int size);
+ *
+ *     {@code @Description}("Last time the configuration was changed, " +
+ *                  "in milliseconds since 1 Jan 1970")
+ *     public long getLastChangedTime();
+ *
+ *     {@code @Description}("Save the configuration to a file")
+ *     public void save(
+ *         {@code @Description}("Optional name of the file, or null for the default name")
+ *         String fileName);
+ * }
+ * 
+ * + *

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.

+ * + *

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"}.

+ * + *

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:

+ * + *
+ * public class Configuration implements ConfigurationMBean {
+ *     {@code @Description}("A Configuration MBean with the default file name")
+ *     public Configuration() {
+ *         this(DEFAULT_FILE_NAME);
+ *     }
+ *
+ *     {@code @Description}("A Configuration MBean with a specified file name")
+ *     public Configuration(
+ *         {@code @Description}("Name of the file the configuration is stored in")
+ *         String fileName) {...}
+ *     ...
+ * }
+ * 
+ * + *

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.

+ * + *
+ * {@code @MBean}
+ * {@code @Description}("Application configuration")
+ * public class Configuration {
+ *     {@code @Description}("A Configuration MBean with the default file name")
+ *     public Configuration() {
+ *         this(DEFAULT_FILE_NAME);
+ *     }
+ *
+ *     {@code @Description}("A Configuration MBean with a specified file name")
+ *     public Configuration(
+ *         {@code @Description}("Name of the file the configuration is stored in")
+ *         String fileName) {...}
+ *
+ *     {@code @ManagedAttribute}
+ *     {@code @Description}("Cache size in bytes")
+ *     public int getCacheSize() {...}
+ *     {@code @ManagedAttribute}
+ *     public void setCacheSize(int size) {...}
+ *
+ *     {@code @ManagedOperation}
+ *     {@code @Description}("Last time the configuration was changed, " +
+ *                  "in milliseconds since 1 Jan 1970")
+ *     public long getLastChangedTime() {...}
+ *
+ *     {@code @ManagedOperation}
+ *     {@code @Description}("Save the configuration to a file")
+ *     public void save(
+ *         {@code @Description}("Optional name of the file, or null for the default name")
+ *         String fileName) {...}
+ *     ...
+ * }
+ * 
+ */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, + ElementType.TYPE}) +public @interface Description { + /** + *

The description.

+ */ + String value(); + + /** + *

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 + * {@code Descriptor} for the annotated item.

+ */ + @DescriptorKey( + value = "descriptionResourceBundleBaseName", omitIfDefault = true) + String bundleBaseName() default ""; + + /** + *

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 + * {@code Descriptor} for the annotated item.

+ */ + @DescriptorKey(value = "descriptionResourceKey", omitIfDefault = true) + String key() default ""; +} diff --git a/jdk/src/share/classes/javax/management/Descriptor.java b/jdk/src/share/classes/javax/management/Descriptor.java index 64bb3eccb23..9aa992486a0 100644 --- a/jdk/src/share/classes/javax/management/Descriptor.java +++ b/jdk/src/share/classes/javax/management/Descriptor.java @@ -38,6 +38,7 @@ import java.util.Arrays; import java.util.ResourceBundle; import javax.management.openmbean.CompositeData; +import javax.management.openmbean.MXBeanMappingFactory; import javax.management.openmbean.OpenMBeanAttributeInfoSupport; import javax.management.openmbean.OpenMBeanOperationInfoSupport; import javax.management.openmbean.OpenMBeanParameterInfoSupport; @@ -117,21 +118,19 @@ import javax.management.openmbean.OpenType; * deprecation, for example {@code "1.3 Replaced by the Capacity * attribute"}. * - * descriptionResource
BundleBaseNameStringAny + * + * descriptionResource
BundleBaseNameStringAny * * The base name for the {@link ResourceBundle} in which the key given in * the {@code descriptionResourceKey} field can be found, for example - * {@code "com.example.myapp.MBeanResources"}. The meaning of this - * field is defined by this specification but the field is not set or - * used by the JMX API itself. + * {@code "com.example.myapp.MBeanResources"}. * - * descriptionResourceKeyStringAny + * + * descriptionResourceKeyStringAny * * A resource key for the description of this element. In * conjunction with the {@code descriptionResourceBundleBaseName}, - * this can be used to find a localized version of the description. - * The meaning of this field is defined by this specification but the - * field is not set or used by the JMX API itself. + * this can be used to find a localized version of the description. * * enabledString * MBeanAttributeInfo
MBeanNotificationInfo
MBeanOperationInfo @@ -216,6 +215,14 @@ import javax.management.openmbean.OpenType; * StandardMBean} class will have this field in its MBeanInfo * Descriptor. * + * mxbeanMappingFactoryClass + * String + * MBeanInfo + * + * The name of the {@link MXBeanMappingFactory} class that was used for this + * MXBean, if it was not the {@linkplain MXBeanMappingFactory#DEFAULT default} + * one. + * * openType{@link OpenType} * MBeanAttributeInfo
MBeanOperationInfo
MBeanParameterInfo * diff --git a/jdk/src/share/classes/javax/management/DescriptorFields.java b/jdk/src/share/classes/javax/management/DescriptorFields.java new file mode 100644 index 00000000000..95a4b3a6df1 --- /dev/null +++ b/jdk/src/share/classes/javax/management/DescriptorFields.java @@ -0,0 +1,137 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

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.

+ * + *

Consider this Standard MBean interface, for example:

+ * + *
+ * public interface CacheControlMBean {
+ *     @DescriptorFields("units=bytes")
+ *     public long getCacheSize();
+ * }
+ * 
+ * + *

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}.

+ * + *

Similarly, if the interface looks like this:

+ * + *
+ * public interface CacheControlMBean {
+ *     @DescriptorFields({"units=bytes", "since=1.5"})
+ *     public long getCacheSize();
+ * }
+ * 
+ * + *

then the resulting {@code Descriptor} will contain the following + * fields:

+ * + * + * + * + * + *
NameValue
units"bytes"
since"1.5"
+ * + *

The {@code @DescriptorFields} annotation can be applied to:

+ * + *
    + *
  • a Standard MBean or MXBean interface; + *
  • a method in such an interface; + *
  • 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); + *
  • a public constructor in the class that implements a Standard MBean + * or MXBean; + *
  • a parameter in such a constructor. + *
+ * + *

Other uses of the annotation will either fail to compile or be + * ignored.

+ * + *

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. + * + *

The Descriptor fields contributed in this way must be consistent + * with each other and with any fields contributed by {@link + * DescriptorKey @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.

+ * + *

The Descriptor resulting from these annotations will be merged + * with any Descriptor fields provided by the implementation, such as + * the {@code + * immutableInfo} field for an MBean. The fields from the annotations + * must be consistent with these fields provided by the implementation.

+ * + *

{@literal @DescriptorFields and @DescriptorKey}

+ * + *

The {@link DescriptorKey @DescriptorKey} annotation provides + * another way to use annotations to define Descriptor fields. + * @DescriptorKey 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. + * @DescriptorFields is more convenient but includes + * those risks. @DescriptorFields 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 @DescriptorKey. + * + * @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 { + /** + *

The descriptor fields. Each element of the string looks like + * {@code "name=value"}.

+ */ + public String[] value(); +} diff --git a/jdk/src/share/classes/javax/management/DescriptorKey.java b/jdk/src/share/classes/javax/management/DescriptorKey.java index ad12612db18..9f919490512 100644 --- a/jdk/src/share/classes/javax/management/DescriptorKey.java +++ b/jdk/src/share/classes/javax/management/DescriptorKey.java @@ -33,6 +33,11 @@ import java.lang.annotation.*; * an MBean, or for an attribute, operation, or constructor in an * MBean, or for a parameter of an operation or constructor.

* + *

(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.)

+ * *

Consider this annotation for example:

* *
@@ -57,7 +62,7 @@ import java.lang.annotation.*;
  * 

When a Standard MBean is made from the {@code CacheControlMBean}, * the usual rules mean that it will have an attribute called * {@code CacheSize} of type {@code long}. The {@code @Units} - * attribute, given the above definition, will ensure that the + * annotation, given the above definition, will ensure that the * {@link MBeanAttributeInfo} for this attribute will have a * {@code Descriptor} that has a field called {@code units} with * corresponding value {@code bytes}.

@@ -125,12 +130,13 @@ import java.lang.annotation.*; * the method in the child interface are considered. * *

The Descriptor fields contributed in this way by different - * annotations on the same program element must be consistent. That - * is, two different annotations, or two members of the same - * annotation, must not define a different value for the same - * Descriptor field. Fields from annotations on a getter method must - * also be consistent with fields from annotations on the - * corresponding setter method.

+ * annotations on the same program element must be consistent with + * each other and with any fields contributed by a {@link + * DescriptorFields @DescriptorFields} annotation. That is, two + * different annotations, or two members of the same annotation, must + * not define a different value for the same Descriptor field. Fields + * from annotations on a getter method must also be consistent with + * fields from annotations on the corresponding setter method.

* *

The Descriptor resulting from these annotations will be merged * with any Descriptor fields provided by the implementation, such as @@ -169,4 +175,36 @@ import java.lang.annotation.*; @Target(ElementType.METHOD) public @interface DescriptorKey { String value(); + + /** + *

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:

+ * + *
+     * @Documented
+     * @Target(ElementType.METHOD)
+     * @Retention(RetentionPolicy.RUNTIME)
+     * public @interface Units {
+     *     @DescriptorKey("units")
+     *     String value();
+     *
+     *     @DescriptorKey(value = "descriptionResourceKey",
+     *                    omitIfDefault = true)
+     *     String resourceKey() default "";
+     *
+     *     @DescriptorKey(value = "descriptionResourceBundleBaseName",
+     *                    omitIfDefault = true)
+     *     String resourceBundleBaseName() default "";
+     * }
+     * 
+ * + *

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.

+ */ + boolean omitIfDefault() default false; } diff --git a/jdk/src/share/classes/javax/management/DynamicWrapperMBean.java b/jdk/src/share/classes/javax/management/DynamicWrapperMBean.java new file mode 100644 index 00000000000..4a67a96795a --- /dev/null +++ b/jdk/src/share/classes/javax/management/DynamicWrapperMBean.java @@ -0,0 +1,62 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management; + +/** + *

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.

+ * + * @see MBeanServer#getClassLoaderFor + * @see MBeanServer#isInstanceOf + */ +public interface DynamicWrapperMBean extends DynamicMBean { + /** + *

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.

+ * + * @return The resource corresponding to this MBean. + */ + public Object getWrappedObject(); + + /** + *

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(); +} diff --git a/jdk/src/share/classes/javax/management/Impact.java b/jdk/src/share/classes/javax/management/Impact.java new file mode 100644 index 00000000000..9416df6fac8 --- /dev/null +++ b/jdk/src/share/classes/javax/management/Impact.java @@ -0,0 +1,105 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management; + +/** + *

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.

+ */ +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; + } + } +} diff --git a/jdk/src/share/classes/javax/management/JMX.java b/jdk/src/share/classes/javax/management/JMX.java index 91e9f455f60..0d75b8c8743 100644 --- a/jdk/src/share/classes/javax/management/JMX.java +++ b/jdk/src/share/classes/javax/management/JMX.java @@ -26,6 +26,7 @@ package javax.management; import com.sun.jmx.mbeanserver.Introspector; +import com.sun.jmx.mbeanserver.MBeanInjector; import com.sun.jmx.remote.util.ClassLogger; import java.beans.BeanInfo; import java.beans.PropertyDescriptor; @@ -130,6 +131,7 @@ public class JMX { *
* * @see javax.management.JMX.ProxyOptions + * @see javax.management.StandardMBean.Options */ public static class MBeanOptions implements Serializable, Cloneable { private static final long serialVersionUID = -6380842449318177843L; @@ -739,4 +741,28 @@ public class JMX { // exactly the string "MXBean" since that would mean there // was no package name, which is pretty unlikely in practice. } + + /** + *

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 resource + * injection to obtain an instance of {@link SendNotification} + * through which it can send notifications.

+ * + * @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)); + } } diff --git a/jdk/src/share/classes/javax/management/MBean.java b/jdk/src/share/classes/javax/management/MBean.java new file mode 100644 index 00000000000..6837740334c --- /dev/null +++ b/jdk/src/share/classes/javax/management/MBean.java @@ -0,0 +1,68 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

Indicates that the annotated class is a Standard MBean. A Standard + * MBean class can be defined as in this example:

+ * + *
+ * {@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();
+ * }
+ * 
+ * + *

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.

+ * + *

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}.

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

Can be implemented by an MBean in order to * carry out operations before and after being registered or unregistered from - * the MBean server. + * the MBean Server. An MBean can also implement this interface in order + * to get a reference to the MBean Server and/or its name within that + * MBean Server.

+ * + *

Resource injection

+ * + *

As an alternative to implementing {@code MBeanRegistration}, if all that + * is needed is the MBean Server or ObjectName then an MBean can use + * resource injection.

+ * + *

If a field in the MBean object has type {@link ObjectName} and has + * the {@link javax.annotation.Resource @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 @Resource annotation, then it will + * be set to the {@code MBeanServer} in which the MBean is registered.

+ * + *

For example:

+ * + *
+ * public Configuration implements ConfigurationMBean {
+ *     @Resource
+ *     private volatile MBeanServer mbeanServer;
+ *     @Resource
+ *     private volatile ObjectName objectName;
+ *     ...
+ *     void unregisterSelf() throws Exception {
+ *         mbeanServer.unregisterMBean(objectName);
+ *     }
+ * }
+ * 
+ * + *

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:

+ * + *
+ * public Configuration implements ConfigurationMBean {
+ *     @Resource
+ *     private volatile SendNotification sender;
+ *     ...
+ *     private void updated() {
+ *         Notification n = new Notification(...);
+ *         sender.sendNotification(n);
+ *     }
+ * }
+ * 
+ * + *

A field to be injected must not be static. It is recommended that + * such fields be declared {@code volatile}.

+ * + *

It is also possible to use the @Resource 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}.

+ * + *

Any number of fields and methods may have the @Resource + * annotation. All fields and methods with type {@code ObjectName} + * (for example) will receive the same {@code ObjectName} value.

+ * + *

Resource injection is available for all types of MBeans, not just + * Standard MBeans.

+ * + *

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. + * + *

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 @Resource 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.

+ * + *

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 @Resource annotation. For example:

+ * + *
+ *     @Resource(type = MBeanServer.class)
+ *     private volatile MBeanServerConnection mbsc;
+ * 
+ * + *

Formally, suppose R is the type in the @Resource + * annotation and T is the type of the method parameter or field. + * Then one of R and T 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 @Resource annotation is ignored.

+ * + *

Resource injection in MBeans is new in version 2.0 of the JMX API.

* * @since 1.5 */ @@ -38,12 +130,12 @@ public interface MBeanRegistration { /** * Allows the MBean to perform any operations it needs before - * being registered in the MBean server. If the name of the MBean + * being registered in the MBean Server. If the name of the MBean * is not specified, the MBean can provide a name for its * registration. If any exception is raised, the MBean will not be - * registered in the MBean server. + * registered in the MBean Server. * - * @param server The MBean server in which the MBean will be registered. + * @param server The MBean Server in which the MBean will be registered. * * @param name The object name of the MBean. This name is null if * the name parameter to one of the createMBean or @@ -57,7 +149,7 @@ public interface MBeanRegistration { * the returned value. * * @exception java.lang.Exception This exception will be caught by - * the MBean server and re-thrown as an {@link + * the MBean Server and re-thrown as an {@link * MBeanRegistrationException}. */ public ObjectName preRegister(MBeanServer server, diff --git a/jdk/src/share/classes/javax/management/MBeanServer.java b/jdk/src/share/classes/javax/management/MBeanServer.java index 728f71340ba..20818bfaaff 100644 --- a/jdk/src/share/classes/javax/management/MBeanServer.java +++ b/jdk/src/share/classes/javax/management/MBeanServer.java @@ -61,7 +61,7 @@ import javax.management.loading.ClassLoaderRepository; * ObjectName is:
* JMImplementation:type=MBeanServerDelegate.

* - *

An object obtained from the {@link + *

An object obtained from the {@link * MBeanServerFactory#createMBeanServer(String) createMBeanServer} or * {@link MBeanServerFactory#newMBeanServer(String) newMBeanServer} * methods of the {@link MBeanServerFactory} class applies security @@ -661,13 +661,16 @@ public interface MBeanServer extends MBeanServerConnection { ReflectionException; /** - *

Return the {@link java.lang.ClassLoader} that was used for - * loading the class of the named MBean.

+ *

Return the {@link java.lang.ClassLoader} that was used for loading + * the class of the named MBean. If the MBean implements the {@link + * DynamicWrapperMBean} interface, then the returned value is the + * result of the {@link DynamicWrapperMBean#getWrappedClassLoader()} + * method.

* * @param mbeanName The ObjectName of the MBean. * * @return The ClassLoader used for that MBean. If l - * is the MBean's actual ClassLoader, and r is the + * is the value specified by the rules above, and r is the * returned value, then either: * *
    diff --git a/jdk/src/share/classes/javax/management/MBeanServerConnection.java b/jdk/src/share/classes/javax/management/MBeanServerConnection.java index 852ab83d890..4047373c261 100644 --- a/jdk/src/share/classes/javax/management/MBeanServerConnection.java +++ b/jdk/src/share/classes/javax/management/MBeanServerConnection.java @@ -839,6 +839,12 @@ public interface MBeanServerConnection { * *

    Otherwise, the result is false.

    * + *

    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 ObjectName of the MBean. * @param className The name of the class. * diff --git a/jdk/src/share/classes/javax/management/MXBean.java b/jdk/src/share/classes/javax/management/MXBean.java index d707bd33058..cf16b18f4d5 100644 --- a/jdk/src/share/classes/javax/management/MXBean.java +++ b/jdk/src/share/classes/javax/management/MXBean.java @@ -27,6 +27,7 @@ package javax.management; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -57,11 +58,13 @@ import javax.management.openmbean.TabularDataSupport; import javax.management.openmbean.TabularType; /** -

    Annotation to mark an interface explicitly as being an MXBean - interface, or as not being an MXBean interface. By default, an +

    Annotation to mark a class or interface explicitly as being an MXBean, + or as not being an MXBean. By default, an interface is an MXBean interface if its name ends with {@code - MXBean}, as in {@code SomethingMXBean}. The following interfaces - are MXBean interfaces:

    + MXBean}, as in {@code SomethingMXBean}. A class is never an MXBean by + default.

    + +

    The following interfaces are MXBean interfaces:

         public interface WhatsitMXBean {}
    @@ -82,6 +85,11 @@ import javax.management.openmbean.TabularType;
         public interface MisleadingMXBean {}
         
    +

    A class can be annotated with {@code @MXBean} to indicate that it + is an MXBean. In this case, its methods should have @{@link + ManagedAttribute} or @{@link ManagedOperation} + annotations, as described for @{@link MBean}.

    +

    MXBean specification

    The MXBean concept provides a simple way to code an MBean @@ -1246,9 +1254,24 @@ public interface Node { @since 1.6 */ +/* + * This annotation is @Inherited because if an MXBean is defined as a + * class using annotations, then its subclasses are also MXBeans. + * For example: + * @MXBean + * public class Super { + * @ManagedAttribute + * public String getName() {...} + * } + * public class Sub extends Super {} + * Here Sub is an MXBean. + * + * The @Inherited annotation has no effect when applied to an interface. + */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) +@Inherited public @interface MXBean { /** True if the annotated interface is an MXBean interface. diff --git a/jdk/src/share/classes/javax/management/ManagedAttribute.java b/jdk/src/share/classes/javax/management/ManagedAttribute.java new file mode 100644 index 00000000000..a8a7299d6f9 --- /dev/null +++ b/jdk/src/share/classes/javax/management/ManagedAttribute.java @@ -0,0 +1,64 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

    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.

    + * + *

    The annotated method must be a getter or setter. In other words, + * it must look like one of the following...

    + * + *
    + * T getFoo()
    + * void setFoo(T param)
    + * 
    + * + *

    ...where {@code T} is any type and {@code Foo} is the + * name of the attribute. For any attribute {@code Foo}, if only + * a {@code get}{@code Foo} method has a {@code ManagedAttribute} + * annotation, then {@code Foo} is a read-only attribute. If only + * a {@code set}{@code Foo} method has a {@code ManagedAttribute} + * annotation, then {@code Foo} is a write-only attribute. If + * both {@code get}{@code Foo} and {@code set}{@code Foo} + * methods have the annotation, then {@code Foo} is a read-write + * attribute. In this last case, the type {@code T} must be the + * same in both methods.

    + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface ManagedAttribute { +} diff --git a/jdk/src/share/classes/javax/management/ManagedOperation.java b/jdk/src/share/classes/javax/management/ManagedOperation.java new file mode 100644 index 00000000000..fa01ac2bff6 --- /dev/null +++ b/jdk/src/share/classes/javax/management/ManagedOperation.java @@ -0,0 +1,67 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

    Indicates that a method in an MBean class defines an MBean operation. + * This annotation can 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.
    • + *
    • A method of an MBean or MXBean interface. + *
    + * + *

    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:

    + * + *
    + * public interface ConfigurationMBean {
    + *     {@code @ManagedOperation}(impact = {@link Impact#ACTION Impact.ACTION})
    + *     public void save();
    + *     ...
    + * }
    + * 
    + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface ManagedOperation { + /** + *

    The impact of this operation, as shown by + * {@link MBeanOperationInfo#getImpact()}. + */ + Impact impact() default Impact.UNKNOWN; +} diff --git a/jdk/src/share/classes/javax/management/NotQueryExp.java b/jdk/src/share/classes/javax/management/NotQueryExp.java index 25bdd5d60d1..ef2dc57b797 100644 --- a/jdk/src/share/classes/javax/management/NotQueryExp.java +++ b/jdk/src/share/classes/javax/management/NotQueryExp.java @@ -91,6 +91,7 @@ class NotQueryExp extends QueryEval implements QueryExp { return "not (" + exp + ")"; } + @Override String toQueryString() { return "not (" + Query.toString(exp) + ")"; } diff --git a/jdk/src/share/classes/javax/management/NotificationBroadcasterSupport.java b/jdk/src/share/classes/javax/management/NotificationBroadcasterSupport.java index 65064ab900e..a358a7012a2 100644 --- a/jdk/src/share/classes/javax/management/NotificationBroadcasterSupport.java +++ b/jdk/src/share/classes/javax/management/NotificationBroadcasterSupport.java @@ -58,7 +58,8 @@ import com.sun.jmx.remote.util.ClassLogger; * * @since 1.5 */ -public class NotificationBroadcasterSupport implements NotificationEmitter { +public class NotificationBroadcasterSupport + implements NotificationEmitter, SendNotification { /** * Constructs a NotificationBroadcasterSupport where each listener is invoked by the * thread sending the notification. This constructor is equivalent to diff --git a/jdk/src/share/classes/javax/management/NotificationInfo.java b/jdk/src/share/classes/javax/management/NotificationInfo.java new file mode 100644 index 00000000000..a899346c06b --- /dev/null +++ b/jdk/src/share/classes/javax/management/NotificationInfo.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

    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"}:

    + * + *
    + * // Example one: a Standard MBean
    + * {@code @NotificationInfo}(types={"com.example.notifs.create",
    + *                          "com.example.notifs.destroy"})
    + * public interface CacheMBean {...}
    + *
    + * public class Cache implements CacheMBean {...}
    + * 
    + * + *
    + * // Example two: an annotated MBean
    + * {@link MBean @MBean}
    + * {@code @NotificationInfo}(types={"com.example.notifs.create",
    + *                          "com.example.notifs.destroy"})
    + * public class Cache {...}
    + * 
    + * + *

    Each {@code @NotificationInfo} produces an {@link + * MBeanNotificationInfo} inside the {@link MBeanInfo} of each MBean + * to which the annotation applies.

    + * + *

    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. + * + *

    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.

    + */ +@Documented +@Inherited +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface NotificationInfo { + /** + *

    The {@linkplain Notification#getType() notification types} + * that this MBean can emit.

    + */ + String[] types(); + + /** + *

    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.

    + */ + Class notificationClass() default Notification.class; + + /** + *

    The description of this notification. For example: + * + *

    +     * {@code @NotificationInfo}(
    +     *         types={"com.example.notifs.create"},
    +     *         description={@code @Description}("object created"))
    +     * 
    + */ + Description description() default @Description(""); + + /** + *

    Additional descriptor fields for the derived {@code + * MBeanNotificationInfo}. They are specified in the same way as + * for the {@link DescriptorFields @DescriptorFields} annotation, + * for example:

    + *
    +     * {@code @NotificationInfo}(
    +     *         types={"com.example.notifs.create"},
    +     *         descriptorFields={"severity=6"})
    +     * 
    + */ + String[] descriptorFields() default {}; +} diff --git a/jdk/src/share/classes/javax/management/NotificationInfos.java b/jdk/src/share/classes/javax/management/NotificationInfos.java new file mode 100644 index 00000000000..9d7c497a78d --- /dev/null +++ b/jdk/src/share/classes/javax/management/NotificationInfos.java @@ -0,0 +1,72 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management; + +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; + +/** + *

    Specifies the kinds of notification an MBean can emit, when this + * cannot be represented by a single {@link NotificationInfo + * @NotificationInfo} annotation.

    + * + *

    For example, this annotation specifies that an MBean can emit + * {@link AttributeChangeNotification} and {@link + * JMXConnectionNotification}:

    + * + *
    + * {@code @NotificationInfos}(
    + *     {@code @NotificationInfo}(
    + *         types = {{@link AttributeChangeNotification#ATTRIBUTE_CHANGE}},
    + *         notificationClass = AttributeChangeNotification.class),
    + *     {@code @NotificationInfo}(
    + *         types = {{@link JMXConnectionNotification#OPENED},
    + *                  {@link JMXConnectionNotification#CLOSED}},
    + *         notificationClass = JMXConnectionNotification.class)
    + * )
    + * 
    + * + *

    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}.

    + */ +@Documented +@Inherited +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface NotificationInfos { + /** + *

    The {@link NotificationInfo} annotations.

    + */ + NotificationInfo[] value(); +} diff --git a/jdk/src/share/classes/javax/management/SendNotification.java b/jdk/src/share/classes/javax/management/SendNotification.java new file mode 100644 index 00000000000..e2875d4f121 --- /dev/null +++ b/jdk/src/share/classes/javax/management/SendNotification.java @@ -0,0 +1,38 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management; + +/** + * Interface implemented by objects that can be asked to send a notification. + */ +public interface SendNotification { + /** + * Sends a notification. + * + * @param notification The notification to send. + */ + public void sendNotification(Notification notification); +} diff --git a/jdk/src/share/classes/javax/management/StandardEmitterMBean.java b/jdk/src/share/classes/javax/management/StandardEmitterMBean.java index 6f2a9b57f82..c3faec374eb 100644 --- a/jdk/src/share/classes/javax/management/StandardEmitterMBean.java +++ b/jdk/src/share/classes/javax/management/StandardEmitterMBean.java @@ -25,6 +25,9 @@ package javax.management; +import com.sun.jmx.mbeanserver.MBeanInjector; +import static javax.management.JMX.MBeanOptions; + /** *

    An MBean whose management interface is determined by reflection * on a Java interface, and that emits notifications.

    @@ -62,7 +65,7 @@ package javax.management; * @since 1.6 */ public class StandardEmitterMBean extends StandardMBean - implements NotificationEmitter { + implements NotificationEmitter, SendNotification { private final NotificationEmitter emitter; private final MBeanNotificationInfo[] notificationInfo; @@ -76,9 +79,10 @@ public class StandardEmitterMBean extends StandardMBean * for {@code implementation} and {@code emitter} to be the same object.

    * *

    If {@code emitter} is an instance of {@code - * NotificationBroadcasterSupport} then the MBean's {@link #sendNotification + * SendNotification} (for example, a {@link NotificationBroadcasterSupport}), + * then the MBean's {@link #sendNotification * sendNotification} method will call {@code emitter.}{@link - * NotificationBroadcasterSupport#sendNotification sendNotification}.

    + * SendNotification#sendNotification sendNotification}.

    * *

    The array returned by {@link #getNotificationInfo()} on the * new MBean is a copy of the array returned by @@ -90,20 +94,18 @@ public class StandardEmitterMBean extends StandardMBean * * @param implementation the implementation of the MBean interface. * @param mbeanInterface a Standard MBean interface. - * @param emitter the object that will handle notifications. + * @param emitter the object that will handle notifications. If null, + * a new {@code NotificationEmitter} will be constructed that also + * implements {@link SendNotification}. * * @throws IllegalArgumentException if the {@code mbeanInterface} * does not follow JMX design patterns for Management Interfaces, or * if the given {@code implementation} does not implement the - * specified interface, or if {@code emitter} is null. + * specified interface. */ public StandardEmitterMBean(T implementation, Class mbeanInterface, NotificationEmitter emitter) { - super(implementation, mbeanInterface, false); - if (emitter == null) - throw new IllegalArgumentException("Null emitter"); - this.emitter = emitter; - this.notificationInfo = emitter.getNotificationInfo(); + this(implementation, mbeanInterface, false, emitter); } /** @@ -118,9 +120,10 @@ public class StandardEmitterMBean extends StandardMBean * same object.

    * *

    If {@code emitter} is an instance of {@code - * NotificationBroadcasterSupport} then the MBean's {@link #sendNotification + * SendNotification} (for example, a {@link NotificationBroadcasterSupport}), + * then the MBean's {@link #sendNotification * sendNotification} method will call {@code emitter.}{@link - * NotificationBroadcasterSupport#sendNotification sendNotification}.

    + * SendNotification#sendNotification sendNotification}.

    * *

    The array returned by {@link #getNotificationInfo()} on the * new MBean is a copy of the array returned by @@ -134,21 +137,69 @@ public class StandardEmitterMBean extends StandardMBean * @param mbeanInterface a Standard MBean interface. * @param isMXBean If true, the {@code mbeanInterface} parameter * names an MXBean interface and the resultant MBean is an MXBean. - * @param emitter the object that will handle notifications. + * @param emitter the object that will handle notifications. If null, + * a new {@code NotificationEmitter} will be constructed that also + * implements {@link SendNotification}. * * @throws IllegalArgumentException if the {@code mbeanInterface} * does not follow JMX design patterns for Management Interfaces, or * if the given {@code implementation} does not implement the - * specified interface, or if {@code emitter} is null. + * specified interface. */ public StandardEmitterMBean(T implementation, Class mbeanInterface, boolean isMXBean, NotificationEmitter emitter) { - super(implementation, mbeanInterface, isMXBean); + this(implementation, mbeanInterface, + isMXBean ? MBeanOptions.MXBEAN : null, emitter); + } + + /** + *

    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.

    + * + *

    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}.

    + * + *

    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()}.

    + * + * @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 StandardEmitterMBean(T implementation, Class mbeanInterface, + MBeanOptions options, + NotificationEmitter emitter) { + super(implementation, mbeanInterface, options); if (emitter == null) - throw new IllegalArgumentException("Null emitter"); + emitter = defaultEmitter(); this.emitter = emitter; this.notificationInfo = emitter.getNotificationInfo(); + injectEmitter(); } /** @@ -159,9 +210,10 @@ public class StandardEmitterMBean extends StandardMBean * by forwarding its methods to {@code emitter}.

    * *

    If {@code emitter} is an instance of {@code - * NotificationBroadcasterSupport} then the MBean's {@link #sendNotification + * SendNotification} (for example, a {@link NotificationBroadcasterSupport}), + * then the MBean's {@link #sendNotification * sendNotification} method will call {@code emitter.}{@link - * NotificationBroadcasterSupport#sendNotification sendNotification}.

    + * SendNotification#sendNotification sendNotification}.

    * *

    The array returned by {@link #getNotificationInfo()} on the * new MBean is a copy of the array returned by @@ -175,20 +227,17 @@ public class StandardEmitterMBean extends StandardMBean * the given {@code mbeanInterface}.

    * * @param mbeanInterface a StandardMBean interface. - * @param emitter the object that will handle notifications. + * @param emitter the object that will handle notifications. If null, + * a new {@code NotificationEmitter} will be constructed that also + * implements {@link SendNotification}. * * @throws IllegalArgumentException if the {@code mbeanInterface} * does not follow JMX design patterns for Management Interfaces, or - * if {@code this} does not implement the specified interface, or - * if {@code emitter} is null. + * if {@code this} does not implement the specified interface. */ protected StandardEmitterMBean(Class mbeanInterface, NotificationEmitter emitter) { - super(mbeanInterface, false); - if (emitter == null) - throw new IllegalArgumentException("Null emitter"); - this.emitter = emitter; - this.notificationInfo = emitter.getNotificationInfo(); + this(mbeanInterface, false, emitter); } /** @@ -200,9 +249,10 @@ public class StandardEmitterMBean extends StandardMBean * forwarding its methods to {@code emitter}.

    * *

    If {@code emitter} is an instance of {@code - * NotificationBroadcasterSupport} then the MBean's {@link #sendNotification + * SendNotification} (for example, a {@link NotificationBroadcasterSupport}), + * then the MBean's {@link #sendNotification * sendNotification} method will call {@code emitter.}{@link - * NotificationBroadcasterSupport#sendNotification sendNotification}.

    + * SendNotification#sendNotification sendNotification}.

    * *

    The array returned by {@link #getNotificationInfo()} on the * new MBean is a copy of the array returned by @@ -218,20 +268,86 @@ public class StandardEmitterMBean extends StandardMBean * @param mbeanInterface a StandardMBean interface. * @param isMXBean If true, the {@code mbeanInterface} parameter * names an MXBean interface and the resultant MBean is an MXBean. - * @param emitter the object that will handle notifications. + * @param emitter the object that will handle notifications. If null, + * a new {@code NotificationEmitter} will be constructed that also + * implements {@link SendNotification}. * * @throws IllegalArgumentException if the {@code mbeanInterface} * does not follow JMX design patterns for Management Interfaces, or - * if {@code this} does not implement the specified interface, or - * if {@code emitter} is null. + * if {@code this} does not implement the specified interface. */ protected StandardEmitterMBean(Class mbeanInterface, boolean isMXBean, NotificationEmitter emitter) { - super(mbeanInterface, isMXBean); + this(mbeanInterface, isMXBean ? MBeanOptions.MXBEAN : null, emitter); + } + + /** + *

    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}.

    + * + *

    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}.

    + * + *

    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()}.

    + * + *

    This constructor must be called from a subclass that implements + * the given {@code mbeanInterface}.

    + * + * @param mbeanInterface a StandardMBean interface. + * @param options MBeanOptions that control the operation of the resulting + * MBean. + * @param emitter the object that will handle notifications. If null, + * a new {@code NotificationEmitter} will be constructed that also + * implements {@link SendNotification}. + * + * @throws IllegalArgumentException if the {@code mbeanInterface} + * does not follow JMX design patterns for Management Interfaces, or + * if {@code this} does not implement the specified interface. + */ + protected StandardEmitterMBean(Class mbeanInterface, MBeanOptions options, + NotificationEmitter emitter) { + super(mbeanInterface, options); if (emitter == null) - throw new IllegalArgumentException("Null emitter"); + emitter = defaultEmitter(); this.emitter = emitter; this.notificationInfo = emitter.getNotificationInfo(); + injectEmitter(); + } + + private NotificationEmitter defaultEmitter() { + MBeanNotificationInfo[] mbnis = getNotificationInfo(); + // Will be null unless getNotificationInfo() is overridden, + // since the notificationInfo field has not been set at this point. + if (mbnis == null) + mbnis = getMBeanInfo().getNotifications(); + return new NotificationBroadcasterSupport(mbnis); + } + + private void injectEmitter() { + if (emitter instanceof SendNotification) { + try { + Object resource = getImplementation(); + SendNotification send = (SendNotification) emitter; + MBeanInjector.injectSendNotification(resource, send); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } } public void removeNotificationListener(NotificationListener listener) @@ -259,10 +375,10 @@ public class StandardEmitterMBean extends StandardMBean /** *

    Sends a notification.

    * - *

    If the {@code emitter} parameter to the constructor was an - * instance of {@code NotificationBroadcasterSupport} then this - * method will call {@code emitter.}{@link - * NotificationBroadcasterSupport#sendNotification + *

    If the {@code emitter} parameter to the constructor was + * an instance of {@link SendNotification}, such as {@link + * NotificationBroadcasterSupport}, then this method will call {@code + * emitter.}{@link SendNotification#sendNotification * sendNotification}.

    * * @param n the notification to send. @@ -271,13 +387,12 @@ public class StandardEmitterMBean extends StandardMBean * constructor was not a {@code NotificationBroadcasterSupport}. */ public void sendNotification(Notification n) { - if (emitter instanceof NotificationBroadcasterSupport) - ((NotificationBroadcasterSupport) emitter).sendNotification(n); + if (emitter instanceof SendNotification) + ((SendNotification) emitter).sendNotification(n); else { final String msg = "Cannot sendNotification when emitter is not an " + - "instance of NotificationBroadcasterSupport: " + - emitter.getClass().getName(); + "instance of SendNotification: " + emitter.getClass().getName(); throw new ClassCastException(msg); } } @@ -292,6 +407,7 @@ public class StandardEmitterMBean extends StandardMBean * @param info The default MBeanInfo derived by reflection. * @return the MBeanNotificationInfo[] for the new MBeanInfo. */ + @Override MBeanNotificationInfo[] getNotifications(MBeanInfo info) { return getNotificationInfo(); } diff --git a/jdk/src/share/classes/javax/management/StandardMBean.java b/jdk/src/share/classes/javax/management/StandardMBean.java index 828a2a87bb1..38b0af416f3 100644 --- a/jdk/src/share/classes/javax/management/StandardMBean.java +++ b/jdk/src/share/classes/javax/management/StandardMBean.java @@ -27,6 +27,7 @@ package javax.management; import com.sun.jmx.mbeanserver.DescriptorCache; import com.sun.jmx.mbeanserver.Introspector; +import com.sun.jmx.mbeanserver.MBeanInjector; import com.sun.jmx.mbeanserver.MBeanSupport; import com.sun.jmx.mbeanserver.MXBeanSupport; import com.sun.jmx.mbeanserver.StandardMBeanSupport; @@ -125,7 +126,78 @@ import static javax.management.JMX.MBeanOptions; * * @since 1.5 */ -public class StandardMBean implements DynamicMBean, MBeanRegistration { +public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration { + + /** + *

    Options controlling the behavior of {@code StandardMBean} instances.

    + */ + public static class Options extends JMX.MBeanOptions { + private static final long serialVersionUID = 5107355471177517164L; + + private boolean wrappedVisible; + + /** + *

    Construct an {@code Options} object where all options have + * their default values.

    + */ + public Options() {} + + @Override + public Options clone() { + return (Options) super.clone(); + } + + /** + *

    Defines whether the {@link StandardMBean#getWrappedObject() + * getWrappedObject} method returns the wrapped object.

    + * + *

    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.

    + * + * @return true if this StandardMBean's {@link + * StandardMBean#getWrappedObject getWrappedObject} returns the wrapped + * object. + */ + public boolean isWrappedObjectVisible() { + return this.wrappedVisible; + } + + /** + *

    Set the {@link #isWrappedObjectVisible WrappedObjectVisible} option + * to the given value.

    + * @param visible the new value. + */ + public void setWrappedObjectVisible(boolean visible) { + this.wrappedVisible = visible; + } + + // Canonical objects for each of (MXBean,!MXBean) x (WVisible,!WVisible) + private static final Options[] CANONICALS = { + new Options(), new Options(), new Options(), new Options(), + }; + static { + CANONICALS[1].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT); + CANONICALS[2].setWrappedObjectVisible(true); + CANONICALS[3].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT); + CANONICALS[3].setWrappedObjectVisible(true); + } + @Override + MBeanOptions[] canonicals() { + return CANONICALS; + } + + @Override + boolean same(MBeanOptions opts) { + return (super.same(opts) && opts instanceof Options && + ((Options) opts).wrappedVisible == wrappedVisible); + } + } private final static DescriptorCache descriptors = DescriptorCache.getInstance(JMX.proof); @@ -347,7 +419,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration { * the management interface associated with the given * implementation. * @param options MBeanOptions that control the operation of the resulting - * MBean, as documented in the {@link MBeanOptions} class. + * MBean. * @param Allows the compiler to check * that {@code implementation} does indeed implement the class * described by {@code mbeanInterface}. The compiler can only @@ -381,7 +453,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration { * @param mbeanInterface The Management Interface exported by this * MBean. * @param options MBeanOptions that control the operation of the resulting - * MBean, as documented in the {@link MBeanOptions} class. + * MBean. * * @exception IllegalArgumentException if the mbeanInterface * does not follow JMX design patterns for Management Interfaces, or @@ -441,7 +513,67 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration { * @see #setImplementation **/ public Object getImplementation() { - return mbean.getResource(); + return mbean.getWrappedObject(); + } + + /** + *

    Get the wrapped implementation object or return this object.

    + * + *

    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}.

    + * + *

    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:

    + * + *
    +     * StandardMBean.Options opts = new StandardMBean.Options();
    +     * opts.setWrappedObjectVisible(true);
    +     * StandardMBean mbean = new StandardMBean(impl, MyMBean.class, opts);
    +     * 
    + * + * @return The wrapped implementation object, or this StandardMBean + * instance. + */ + public Object getWrappedObject() { + if (options instanceof Options && + ((Options) options).isWrappedObjectVisible()) + return getImplementation(); + else + return this; + } + + /** + *

    Get the ClassLoader of the wrapped implementation object or of this + * object.

    + * + *

    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()}.

    + * + *

    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:

    + * + *
    +     * StandardMBean.Options opts = new StandardMBean.Options();
    +     * opts.setWrappedObjectVisible(true);
    +     * StandardMBean mbean = new StandardMBean(impl, MyMBean.class, opts);
    +     * 
    + * + * @return The ClassLoader of the wrapped Cimplementation object, or of + * this StandardMBean instance. + */ + public ClassLoader getWrappedClassLoader() { + return getWrappedObject().getClass().getClassLoader(); } /** @@ -457,7 +589,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration { * @return The class of the implementation of this Standard MBean (or MXBean). **/ public Class getImplementationClass() { - return mbean.getResource().getClass(); + return mbean.getWrappedObject().getClass(); } /** @@ -559,7 +691,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration { MBeanSupport msupport = mbean; final MBeanInfo bi = msupport.getMBeanInfo(); - final Object impl = msupport.getResource(); + final Object impl = msupport.getWrappedObject(); final boolean immutableInfo = immutableInfo(this.getClass()); @@ -1184,6 +1316,7 @@ public class StandardMBean implements DynamicMBean, MBeanRegistration { public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { mbean.register(server, name); + MBeanInjector.inject(mbean.getWrappedObject(), server, name); return name; } diff --git a/jdk/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java b/jdk/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java index 6a72302ea20..7470cd52c5e 100644 --- a/jdk/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java +++ b/jdk/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java @@ -23,7 +23,7 @@ * have any questions. */ /* - * @author IBM Corp. + * @(#)author IBM Corp. * * Copyright IBM Corp. 1999-2000. All rights reserved. */ @@ -55,6 +55,7 @@ import javax.management.AttributeChangeNotificationFilter; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.Descriptor; +import javax.management.DynamicWrapperMBean; import javax.management.InstanceNotFoundException; import javax.management.InvalidAttributeValueException; import javax.management.ListenerNotFoundException; @@ -115,7 +116,7 @@ import sun.reflect.misc.ReflectUtil; */ public class RequiredModelMBean - implements ModelMBean, MBeanRegistration, NotificationEmitter { + implements ModelMBean, MBeanRegistration, NotificationEmitter, DynamicWrapperMBean { /*************************************/ /* attributes */ @@ -133,6 +134,9 @@ public class RequiredModelMBean * and operations will be executed */ private Object managedResource = null; + /* true if getWrappedObject returns the wrapped resource */ + private boolean visible; + /* records the registering in MBeanServer */ private boolean registered = false; private transient MBeanServer server = null; @@ -318,9 +322,13 @@ public class RequiredModelMBean * * @param mr Object that is the managed resource * @param mr_type The type of reference for the managed resource. - *
    Can be: "ObjectReference", "Handle", "IOR", "EJBHandle", - * or "RMIReference". - *
    In this implementation only "ObjectReference" is supported. + *
    Can be: "ObjectReference", "VisibleObjectReference", + * "Handle", "IOR", "EJBHandle", or "RMIReference". + *
    In this implementation only "ObjectReference" and + * "VisibleObjectReference" are supported. The two + * types are equivalent except for the behavior of the + * {@link #getWrappedObject()} and {@link #getWrappedClassLoader()} + * methods. * * @exception MBeanException The initializer of the object has * thrown an exception. @@ -340,10 +348,11 @@ public class RequiredModelMBean "setManagedResource(Object,String)","Entry"); } + visible = "visibleObjectReference".equalsIgnoreCase(mr_type); + // check that the mr_type is supported by this JMXAgent // only "objectReference" is supported - if ((mr_type == null) || - (! mr_type.equalsIgnoreCase("objectReference"))) { + if (!"objectReference".equalsIgnoreCase(mr_type) && !visible) { if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { MODELMBEAN_LOGGER.logp(Level.FINER, RequiredModelMBean.class.getName(), @@ -368,6 +377,51 @@ public class RequiredModelMBean } } + /** + *

    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.

    + * + * @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; + } + + /** + *

    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.

    + * + * @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)); + } + /** *

    Instantiates this MBean instance with the data found for * the MBean in the persistent store. The data loaded could include diff --git a/jdk/src/share/classes/javax/management/monitor/package.html b/jdk/src/share/classes/javax/management/monitor/package.html index 5ccb72c9660..2e04b2422fc 100644 --- a/jdk/src/share/classes/javax/management/monitor/package.html +++ b/jdk/src/share/classes/javax/management/monitor/package.html @@ -38,14 +38,17 @@ have any questions. so within the access control context of the {@link javax.management.monitor.Monitor#start} caller.

    -

    The value being monitored can be a simple value contained within a - complex type. For example, the {@link java.lang.management.MemoryMXBean - MemoryMXBean} defined in java.lang.management has an attribute - HeapMemoryUsage of type {@link java.lang.management.MemoryUsage - MemoryUsage}. To monitor the amount of used memory, described by - the used property of MemoryUsage, you could monitor - "HeapMemoryUsage.used". That string would be the argument to - {@link javax.management.monitor.MonitorMBean#setObservedAttribute(String) +

    The value being monitored can be a simple value + contained within a complex type. For example, the {@link + java.lang.management.MemoryMXBean MemoryMXBean} defined in + java.lang.management has an attribute + HeapMemoryUsage of type {@link + java.lang.management.MemoryUsage MemoryUsage}. To monitor the + amount of used memory, described by the used + property of MemoryUsage, you could monitor + "HeapMemoryUsage.used". That string would be the + argument to {@link + javax.management.monitor.MonitorMBean#setObservedAttribute(String) setObservedAttribute}.

    The rules used to interpret an ObservedAttribute like diff --git a/jdk/src/share/classes/javax/management/package.html b/jdk/src/share/classes/javax/management/package.html index 8d6205c2411..14027d648ea 100644 --- a/jdk/src/share/classes/javax/management/package.html +++ b/jdk/src/share/classes/javax/management/package.html @@ -56,41 +56,41 @@ have any questions. resource. It has a management interface consisting of:

    -
      -
    • named and typed attributes that can be read and/or - written
    • - -
    • named and typed operations that can be invoked
    • +
        +
      • named and typed attributes that can be read and/or + written
      • -
      • typed notifications that can be emitted by the MBean.
      • -
      +
    • named and typed operations that can be invoked
    • -

      For example, an MBean representing an application's - configuration could have attributes representing the different - configuration items. Reading the CacheSize - attribute would return the current value of that item. - Writing it would update the item, potentially changing the - behavior of the running application. An operation such as - save could store the current configuration - persistently. A notification such as - ConfigurationChangedNotification could be sent - every time the configuration is changed.

      +
    • typed notifications that can be emitted by the MBean.
    • +
    -

    In the standard usage of the JMX API, MBeans are implemented - as Java objects. However, as explained below, these objects are - not usually referenced directly.

    +

    For example, an MBean representing an application's + configuration could have attributes representing the different + configuration items. Reading the CacheSize + attribute would return the current value of that item. + Writing it would update the item, potentially changing the + behavior of the running application. An operation such as + save could store the current configuration + persistently. A notification such as + ConfigurationChangedNotification could be sent + every time the configuration is changed.

    + +

    In the standard usage of the JMX API, MBeans are implemented + as Java objects. However, as explained below, these objects are + not usually referenced directly.

    -

    Standard MBeans

    +

    Standard MBeans

    -

    To make MBean implementation simple, the JMX API includes the - notion of Standard MBeans. A Standard MBean is one - whose attributes and operations are deduced from a Java - interface using certain naming patterns, similar to those used - by JavaBeansTM. For - example, consider an interface like this:

    +

    To make MBean implementation simple, the JMX API includes the + notion of Standard MBeans. A Standard MBean is one + whose attributes and operations are deduced from a Java + interface using certain naming patterns, similar to those used + by JavaBeansTM. For + example, consider an interface like this:

    -
    +        
         public interface ConfigurationMBean {
     	public int getCacheSize();
     	public void setCacheSize(int size);
    @@ -128,107 +128,148 @@ have any questions.
     	class.

    -

    MXBeans

    - -

    An MXBean is a variant of Standard MBean where complex - types are mapped to a standard set of types defined in the - {@link javax.management.openmbean} package. MXBeans are appropriate - if you would otherwise need to reference application-specific - classes in your MBean interface. They are described in detail - in the specification for {@link javax.management.MXBean MXBean}. +

    Defining Standard MBeans with annotations

    + +

    As an alternative to creating an interface such as + ConfigurationMBean and a class that implements it, + you can write just the class, and use annotations to pick out the + public methods that will make up the management interface. For + example, the following class has the same management interface + as a Configuration class that implements the + ConfigurationMBean interface above.

    + +
    +    {@link javax.management.MBean @MBean}
    +    public class Configuration {
    +        {@link javax.management.ManagedAttribute @ManagedAttribute}
    +        public int getCacheSize() {...}
    +        @ManagedAttribute
    +        public void setCacheSize(int size) {...}
    +
    +        @ManagedAttribute
    +        public long getLastChangedTime() {...}
    +
    +        {@link javax.management.ManagedOperation @ManagedOperation}
    +        public void save() {...}
    +        ...
    +    }
    +        
    + +

    This approach simplifies development, but it does have two + potential drawbacks. First, if you run the Javadoc tool on + this class, the documentation of the management interface may + be mixed in with the documentation of non-management methods + in the class. Second, you cannot make a proxy + as described below if you do not have an + interface like ConfigurationMBean.

    -

    Dynamic MBeans

    +

    MXBeans

    -

    A Dynamic MBean is an MBean that defines its - management interface at run-time. For example, a configuration - MBean could determine the names and types of the attributes it - exposes by parsing an XML file.

    +

    An MXBean is a variant of Standard MBean where complex + types are mapped to a standard set of types defined in the + {@link javax.management.openmbean} package. MXBeans are appropriate + if you would otherwise need to reference application-specific + classes in your MBean interface. They are described in detail + in the specification for {@link javax.management.MXBean MXBean}.

    -

    Any Java object of a class that implements the {@link - javax.management.DynamicMBean DynamicMBean} interface is a - Dynamic MBean.

    +

    You can define MXBeans using annotations as described + in the previous section, but + using the @MXBean annotation instead of + @MBean.

    -

    Open MBeans

    +

    Dynamic MBeans

    -

    An Open MBean is a kind of Dynamic MBean where the - types of attributes and of operation parameters and return - values are built using a small set of predefined Java classes. - Open MBeans facilitate operation with remote management programs - that do not necessarily have access to application-specific - types, including non-Java programs. Open MBeans are defined by - the package - javax.management.openmbean.

    +

    A Dynamic MBean is an MBean that defines its + management interface at run-time. For example, a configuration + MBean could determine the names and types of the attributes it + exposes by parsing an XML file.

    + +

    Any Java object of a class that implements the {@link + javax.management.DynamicMBean DynamicMBean} interface is a + Dynamic MBean.

    -

    Model MBeans

    +

    Open MBeans

    -

    A Model MBean is a kind of Dynamic MBean that acts - as a bridge between the management interface and the - underlying managed resource. Both the management interface and - the managed resource are specified as Java objects. The same - Model MBean implementation can be reused many times with - different management interfaces and managed resources, and it can - provide common functionality such as persistence and caching. - Model MBeans are defined by the package - - javax.management.modelmbean.

    +

    An Open MBean is a kind of Dynamic MBean where the + types of attributes and of operation parameters and return + values are built using a small set of predefined Java classes. + Open MBeans facilitate operation with remote management programs + that do not necessarily have access to application-specific + types, including non-Java programs. Open MBeans are defined by + the package + javax.management.openmbean.

    -

    MBean Server

    - -

    To be useful, an MBean must be registered in an MBean - Server. An MBean Server is a repository of MBeans. - Usually the only access to the MBeans is through the MBean - Server. In other words, code no longer accesses the Java - object implementing the MBean directly, but instead accesses - the MBean by name through the MBean Server. Each MBean has a - unique name within the MBean Server, defined by the {@link - javax.management.ObjectName ObjectName} class.

    - -

    An MBean Server is an object implementing the interface - {@link javax.management.MBeanServer MBeanServer}. - The most convenient MBean Server to use is the - Platform MBean Server. This is a - single MBean Server that can be shared by different managed - components running within the same Java Virtual Machine. The - Platform MBean Server is accessed with the method {@link - java.lang.management.ManagementFactory#getPlatformMBeanServer()}.

    +

    Model MBeans

    -

    Application code can also create a new MBean Server, or - access already-created MBean Servers, using the {@link - javax.management.MBeanServerFactory MBeanServerFactory} class.

    +

    A Model MBean is a kind of Dynamic MBean that acts + as a bridge between the management interface and the + underlying managed resource. Both the management interface and + the managed resource are specified as Java objects. The same + Model MBean implementation can be reused many times with + different management interfaces and managed resources, and it can + provide common functionality such as persistence and caching. + Model MBeans are defined by the package + + javax.management.modelmbean.

    -

    Creating MBeans in the MBean Server

    +

    MBean Server

    -

    There are two ways to create an MBean. One is to construct a - Java object that will be the MBean, then use the {@link - javax.management.MBeanServer#registerMBean registerMBean} - method to register it in the MBean Server. The other is to - create and register the MBean in a single operation using one - of the {@link javax.management.MBeanServer#createMBean(String, - javax.management.ObjectName) createMBean} methods.

    +

    To be useful, an MBean must be registered in an MBean + Server. An MBean Server is a repository of MBeans. + Usually the only access to the MBeans is through the MBean + Server. In other words, code no longer accesses the Java + object implementing the MBean directly, but instead accesses + the MBean by name through the MBean Server. Each MBean has a + unique name within the MBean Server, defined by the {@link + javax.management.ObjectName ObjectName} class.

    -

    The registerMBean method is simpler for local - use, but cannot be used remotely. The - createMBean method can be used remotely, but - sometimes requires attention to class loading issues.

    +

    An MBean Server is an object implementing the interface + {@link javax.management.MBeanServer MBeanServer}. + The most convenient MBean Server to use is the + Platform MBean Server. This is a + single MBean Server that can be shared by different managed + components running within the same Java Virtual Machine. The + Platform MBean Server is accessed with the method {@link + java.lang.management.ManagementFactory#getPlatformMBeanServer()}.

    -

    An MBean can perform actions when it is registered in or - unregistered from an MBean Server if it implements the {@link - javax.management.MBeanRegistration MBeanRegistration} - interface.

    +

    Application code can also create a new MBean Server, or + access already-created MBean Servers, using the {@link + javax.management.MBeanServerFactory MBeanServerFactory} class.

    -

    Accessing MBeans in the MBean Server

    +

    Creating MBeans in the MBean Server

    -

    Given an ObjectName name and an - MBeanServer mbs, you can access - attributes and operations as in this example:

    +

    There are two ways to create an MBean. One is to construct a + Java object that will be the MBean, then use the {@link + javax.management.MBeanServer#registerMBean registerMBean} + method to register it in the MBean Server. The other is to + create and register the MBean in a single operation using one + of the {@link javax.management.MBeanServer#createMBean(String, + javax.management.ObjectName) createMBean} methods.

    -
    +        

    The registerMBean method is simpler for local + use, but cannot be used remotely. The + createMBean method can be used remotely, but + sometimes requires attention to class loading issues.

    + +

    An MBean can perform actions when it is registered in or + unregistered from an MBean Server if it implements the {@link + javax.management.MBeanRegistration MBeanRegistration} + interface.

    + + +

    Accessing MBeans in the MBean Server

    + +

    Given an ObjectName name and an + MBeanServer mbs, you can access + attributes and operations as in this example:

    + +
         int cacheSize = mbs.getAttribute(name, "CacheSize");
         {@link javax.management.Attribute Attribute} newCacheSize =
         	new Attribute("CacheSize", new Integer(2000));
    @@ -236,9 +277,9 @@ have any questions.
         mbs.invoke(name, "save", new Object[0], new Class[0]);
           
    -

    Alternatively, if you have a Java interface that corresponds - to the management interface for the MBean, you can use an - MBean proxy like this:

    +

    Alternatively, if you have a Java interface that + corresponds to the management interface for the MBean, you can use an + MBean proxy like this:

         ConfigurationMBean conf =
    @@ -264,66 +305,116 @@ have any questions.
     	perform the query.

    -

    Notifications

    +

    MBean lifecycle and resource injection

    -

    A notification is an instance of the {@link - javax.management.Notification Notification} class or a - subclass. In addition to its Java class, it has a - type string that can distinguish it from other - notifications of the same class.

    +

    An MBean can implement the {@link javax.management.MBeanRegistration + MBeanRegistration} interface in order to be told when it is registered + and unregistered in the MBean Server. Additionally, the {@link + javax.management.MBeanRegistration#preRegister preRegister} method + allows the MBean to get a reference to the MBeanServer + object and to get its ObjectName within the MBean + Server.

    -

    An MBean that will emit notifications must implement the - {@link javax.management.NotificationBroadcaster - NotificationBroadcaster} or {@link - javax.management.NotificationEmitter NotificationEmitter} - interface. Usually, it does this by subclassing {@link - javax.management.NotificationBroadcasterSupport - NotificationBroadcasterSupport} or by delegating to an instance - of that class.

    - -

    Notifications can be received by a listener, which - is an object that implements the {@link - javax.management.NotificationListener NotificationListener} - interface. You can add a listener to an MBean with the method - {@link - javax.management.MBeanServer#addNotificationListener(ObjectName, - NotificationListener, NotificationFilter, Object)}. - You can optionally supply a filter to this method, to - select only notifications of interest. A filter is an object - that implements the {@link javax.management.NotificationFilter - NotificationFilter} interface.

    - -

    An MBean can be a listener for notifications emitted by other - MBeans in the same MBean Server. In this case, it implements - {@link javax.management.NotificationListener - NotificationListener} and the method {@link - javax.management.MBeanServer#addNotificationListener(ObjectName, - ObjectName, NotificationFilter, Object)} is used to listen.

    +

    If the only reason to implement MBeanRegistration is to + discover the MBeanServer and ObjectName, resource injection may be + more convenient.

    -

    Remote Access to MBeans

    +

    Notifications

    -

    An MBean Server can be accessed remotely through a - connector. A connector allows a remote Java - application to access an MBean Server in essentially the same - way as a local one. The package - - javax.management.remote defines connectors.

    +

    A notification is an instance of the {@link + javax.management.Notification Notification} class or a + subclass. In addition to its Java class, it has a + type string that can distinguish it from other + notifications of the same class.

    -

    The JMX specification also defines the notion of an - adaptor. An adaptor translates between requests in a - protocol such as SNMP or HTML and accesses to an MBean Server. - So for example an SNMP GET operation might result in a - getAttribute on the MBean Server.

    +

    If an MBean is to emit notifications, it must do one of two things.

    -

    - @see - Java SE 6 Platform documentation on JMX technology - in particular the - - JMX Specification, version 1.4(pdf). +

      +
    • It can implement the interface {@link + javax.management.NotificationEmitter NotificationEmitter} (or + its parent {@link javax.management.NotificationBroadcaster + NotificationBroadcaster}), usually by subclassing + {@link javax.management.NotificationBroadcasterSupport + NotificationBroadcasterSupport} or delegating to an instance of + that class.
    • +
    • It can use resource + injection to obtain a {@link javax.management.SendNotification + SendNotification} object that it can use to send + notifications.
    • +
    - @since 1.5 +

    The two classes below illustrate these two techniques:

    + +
    +    // Implementing NotificationEmitter (via NotificationBroadcasterSupport)
    +    public class Configuration extends NotificationBroadcasterSupport
    +            implements ConfigurationMBean {
    +        ...
    +        private void updated() {
    +            Notification n = new Notification(...);
    +            {@link javax.management.NotificationBroadcasterSupport#sendNotification
    +            sendNotification}(n);
    +        }
    +    }
    +
    +    // Getting a SendNotification through resource injection
    +    public class Configuration implements ConfigurationMBean {
    +        @Resource
    +        private volatile SendNotification sender;
    +        ...
    +        private void updated() {
    +            Notification n = new Notification(...);
    +            sender.sendNotification(n);
    +        }
    +    }
    +        
    + + +

    Notifications can be received by a listener, which + is an object that implements the {@link + javax.management.NotificationListener NotificationListener} + interface. You can add a listener to an MBean with the method + {@link + javax.management.MBeanServer#addNotificationListener(ObjectName, + NotificationListener, NotificationFilter, Object)}. + You can optionally supply a filter to this method, to + select only notifications of interest. A filter is an object + that implements the {@link javax.management.NotificationFilter + NotificationFilter} interface.

    + +

    An MBean can be a listener for notifications emitted by other + MBeans in the same MBean Server. In this case, it implements + {@link javax.management.NotificationListener + NotificationListener} and the method {@link + javax.management.MBeanServer#addNotificationListener(ObjectName, + ObjectName, NotificationFilter, Object)} is used to listen.

    + + +

    Remote Access to MBeans

    + +

    An MBean Server can be accessed remotely through a + connector. A connector allows a remote Java + application to access an MBean Server in essentially the same + way as a local one. The package + + javax.management.remote defines connectors.

    + +

    The JMX specification also defines the notion of an + adaptor. An adaptor translates between requests in a + protocol such as SNMP or HTML and accesses to an MBean Server. + So for example an SNMP GET operation might result in a + getAttribute on the MBean Server.

    + +

    + @see + Java SE 6 Platform documentation on JMX technology + in particular the + + JMX Specification, version 1.4(pdf). + + @since 1.5 diff --git a/jdk/test/javax/management/Introspector/AnnotatedMBeanTest.java b/jdk/test/javax/management/Introspector/AnnotatedMBeanTest.java new file mode 100644 index 00000000000..b0782d39c85 --- /dev/null +++ b/jdk/test/javax/management/Introspector/AnnotatedMBeanTest.java @@ -0,0 +1,337 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test %M% %I% + * @bug 6323980 + * @summary Test MBeans defined with @MBean + * @author Eamonn McManus + * @run main/othervm -ea AnnotatedMBeanTest + */ + +import java.io.File; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import javax.management.Attribute; +import javax.management.Descriptor; +import javax.management.DescriptorKey; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MXBean; +import javax.management.MalformedObjectNameException; +import javax.management.ManagedAttribute; +import javax.management.ManagedOperation; +import javax.management.MBean; +import javax.management.ObjectName; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; + +public class AnnotatedMBeanTest { + private static MBeanServer mbs; + private static final ObjectName objectName; + static { + try { + objectName = new ObjectName("test:type=Test"); + } catch (MalformedObjectNameException e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) throws Exception { + if (!AnnotatedMBeanTest.class.desiredAssertionStatus()) + throw new Exception("Test must be run with -ea"); + + File policyFile = File.createTempFile("jmxperms", ".policy"); + policyFile.deleteOnExit(); + PrintWriter pw = new PrintWriter(policyFile); + pw.println("grant {"); + pw.println(" permission javax.management.MBeanPermission \"*\", \"*\";"); + pw.println(" permission javax.management.MBeanServerPermission \"*\";"); + pw.println(" permission javax.management.MBeanTrustPermission \"*\";"); + pw.println("};"); + pw.close(); + + System.setProperty("java.security.policy", policyFile.getAbsolutePath()); + System.setSecurityManager(new SecurityManager()); + + String failure = null; + + for (Method m : AnnotatedMBeanTest.class.getDeclaredMethods()) { + if (Modifier.isStatic(m.getModifiers()) && + m.getName().startsWith("test") && + m.getParameterTypes().length == 0) { + mbs = MBeanServerFactory.newMBeanServer(); + try { + m.invoke(null); + System.out.println(m.getName() + " OK"); + } catch (InvocationTargetException ite) { + System.out.println(m.getName() + " got exception:"); + Throwable t = ite.getCause(); + t.printStackTrace(System.out); + failure = m.getName() + ": " + t.toString(); + } + } + } + if (failure == null) + System.out.println("TEST PASSED"); + else + throw new Exception("TEST FAILED: " + failure); + } + + public static class Stats { + private final int used; + private final int size; + private final boolean interesting; + + public Stats(int used, int size, boolean interesting) { + this.used = used; + this.size = size; + this.interesting = interesting; + } + + public int getUsed() { + return used; + } + + public int getSize() { + return size; + } + + public boolean isInteresting() { + return interesting; + } + } + + @Retention(RetentionPolicy.RUNTIME) + public static @interface Units { + @DescriptorKey("units") + String value(); + } + + @MBean + public static class Cache { + private int used = 23; + private int size = 99; + + @ManagedAttribute + @Units("bytes") + public int getUsed() { + return used; + } + + @ManagedAttribute + public int getSize() { + return size; + } + + @ManagedAttribute + public void setSize(int x) { + this.size = x; + } + + @ManagedAttribute + public boolean isInteresting() { + return false; + } + + @ManagedAttribute + public Stats getStats() { + return new Stats(used, size, false); + } + + @ManagedOperation + public int dropOldest(int n) { + return 55; + } + + private void irrelevantMethod() {} + private int getIrrelevant() {return 0;} + public int getIrrelevant2() {return 0;} + + public int otherIrrelevantMethod() {return 5;} + } + + public static class SubCache extends Cache { + // SubCache does not have the @MBean annotation + // but its parent does. It doesn't add any @ManagedAttribute or + // @ManagedOperation methods, so its management interface + // should be the same. + private void irrelevantMethod2() {} + public int otherIrrelevantMethod3() {return 0;} + + public int getX() {return 0;} + public void setX(int x) {} + } + + @MXBean + public static class CacheMX { + private int used = 23; + private int size = 99; + + @ManagedAttribute + @Units("bytes") + public int getUsed() { + return used; + } + + @ManagedAttribute + public int getSize() { + return size; + } + + @ManagedAttribute + public void setSize(int x) { + this.size = x; + } + + @ManagedAttribute + public boolean isInteresting() { + return false; + } + + @ManagedAttribute + public Stats getStats() { + return new Stats(used, size, false); + } + + @ManagedOperation + public int dropOldest(int n) { + return 55; + } + + private void irrelevantMethod() {} + private int getIrrelevant() {return 0;} + public int getIrrelevant2() {return 0;} + + public int otherIrrelevantMethod() {return 5;} + } + + public static class SubCacheMX extends CacheMX { + private void irrelevantMethod2() {} + public int otherIrrelevantMethod3() {return 0;} + + public int getX() {return 0;} + public void setX(int x) {} + } + + private static void testSimpleManagedResource() throws Exception { + testResource(new Cache(), false); + } + + private static void testSubclassManagedResource() throws Exception { + testResource(new SubCache(), false); + } + + private static void testMXBeanResource() throws Exception { + testResource(new CacheMX(), true); + } + + private static void testSubclassMXBeanResource() throws Exception { + testResource(new SubCacheMX(), true); + } + + private static void testResource(Object resource, boolean mx) throws Exception { + mbs.registerMBean(resource, objectName); + + MBeanInfo mbi = mbs.getMBeanInfo(objectName); + assert mbi.getDescriptor().getFieldValue("mxbean").equals(Boolean.toString(mx)); + + MBeanAttributeInfo[] mbais = mbi.getAttributes(); + + assert mbais.length == 4: mbais.length; + + for (MBeanAttributeInfo mbai : mbais) { + String name = mbai.getName(); + if (name.equals("Used")) { + assert mbai.isReadable(); + assert !mbai.isWritable(); + assert !mbai.isIs(); + assert mbai.getType().equals("int"); + assert "bytes".equals(mbai.getDescriptor().getFieldValue("units")); + } else if (name.equals("Size")) { + assert mbai.isReadable(); + assert mbai.isWritable(); + assert !mbai.isIs(); + assert mbai.getType().equals("int"); + } else if (name.equals("Interesting")) { + assert mbai.isReadable(); + assert !mbai.isWritable(); + assert mbai.isIs(); + assert mbai.getType().equals("boolean"); + } else if (name.equals("Stats")) { + assert mbai.isReadable(); + assert !mbai.isWritable(); + assert !mbai.isIs(); + Descriptor d = mbai.getDescriptor(); + if (mx) { + assert mbai.getType().equals(CompositeData.class.getName()); + assert d.getFieldValue("originalType").equals(Stats.class.getName()); + CompositeType ct = (CompositeType) d.getFieldValue("openType"); + Set names = new HashSet( + Arrays.asList("used", "size", "interesting")); + assert ct.keySet().equals(names) : ct.keySet(); + } else { + assert mbai.getType().equals(Stats.class.getName()); + } + } else + assert false : name; + } + + MBeanOperationInfo[] mbois = mbi.getOperations(); + + assert mbois.length == 1: mbois.length; + + MBeanOperationInfo mboi = mbois[0]; + assert mboi.getName().equals("dropOldest"); + assert mboi.getReturnType().equals("int"); + MBeanParameterInfo[] mbpis = mboi.getSignature(); + assert mbpis.length == 1: mbpis.length; + assert mbpis[0].getType().equals("int"); + + assert mbs.getAttribute(objectName, "Used").equals(23); + + assert mbs.getAttribute(objectName, "Size").equals(99); + mbs.setAttribute(objectName, new Attribute("Size", 55)); + assert mbs.getAttribute(objectName, "Size").equals(55); + + assert mbs.getAttribute(objectName, "Interesting").equals(false); + + Object stats = mbs.getAttribute(objectName, "Stats"); + assert (mx ? CompositeData.class : Stats.class).isInstance(stats) : stats.getClass(); + + int ret = (Integer) mbs.invoke( + objectName, "dropOldest", new Object[] {66}, new String[] {"int"}); + assert ret == 55; + } +} diff --git a/jdk/test/javax/management/Introspector/AnnotatedNotificationInfoTest.java b/jdk/test/javax/management/Introspector/AnnotatedNotificationInfoTest.java new file mode 100644 index 00000000000..bc6a335dfbe --- /dev/null +++ b/jdk/test/javax/management/Introspector/AnnotatedNotificationInfoTest.java @@ -0,0 +1,271 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test %M% %I% + * @bug 6323980 + * @summary Test @NotificationInfo annotation + * @author Eamonn McManus + * @run main/othervm -ea AnnotatedNotificationInfoTest + */ + +import java.io.Serializable; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Field; +import javax.annotation.Resource; +import javax.management.AttributeChangeNotification; +import javax.management.Description; +import javax.management.Descriptor; +import javax.management.ImmutableDescriptor; +import javax.management.MBean; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanServer; +import javax.management.MXBean; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationInfo; +import javax.management.NotificationInfos; +import javax.management.ObjectName; +import javax.management.SendNotification; + +public class AnnotatedNotificationInfoTest { + // Data for the first test. This tests that MBeanNotificationInfo + // is correctly derived from @NotificationInfo. + // Every static field called mbean* is expected to be an MBean + // with a single MBeanNotificationInfo that has the same value + // in each case. + + @NotificationInfo( + types = {"foo", "bar"}, + notificationClass = AttributeChangeNotification.class, + description = @Description( + value = "description", + bundleBaseName = "bundle", + key = "key"), + descriptorFields = {"foo=bar"}) + public static interface Intf1MBean {} + + public static class Intf1 + extends NotificationBroadcasterSupport implements Intf1MBean {} + + private static Object mbeanIntf1 = new Intf1(); + + @NotificationInfos( + @NotificationInfo( + types = {"foo", "bar"}, + notificationClass = AttributeChangeNotification.class, + description = @Description( + value = "description", + bundleBaseName = "bundle", + key = "key"), + descriptorFields = {"foo=bar"})) + public static interface Intf2MBean {} + + public static class Intf2 + extends NotificationBroadcasterSupport implements Intf2MBean {} + + private static Object mbeanIntf2 = new Intf2(); + + @NotificationInfos({}) + @NotificationInfo( + types = {"foo", "bar"}, + notificationClass = AttributeChangeNotification.class, + description = @Description( + value = "description", + bundleBaseName = "bundle", + key = "key"), + descriptorFields = {"foo=bar"}) + public static interface Intf3MBean {} + + public static class Intf3 + extends NotificationBroadcasterSupport implements Intf3MBean {} + + private static Object mbeanIntf3 = new Intf3(); + + @NotificationInfo( + types = {"foo", "bar"}, + notificationClass = AttributeChangeNotification.class, + description = @Description( + value = "description", + bundleBaseName = "bundle", + key = "key"), + descriptorFields = {"foo=bar"}) + public static interface ParentIntf {} + + public static interface Intf4MBean extends Serializable, ParentIntf, Cloneable {} + + public static class Intf4 + extends NotificationBroadcasterSupport implements Intf4MBean {} + + private static Object mbeanIntf4 = new Intf4(); + + @NotificationInfo( + types = {"foo", "bar"}, + notificationClass = AttributeChangeNotification.class, + description = @Description( + value = "description", + bundleBaseName = "bundle", + key = "key"), + descriptorFields = {"foo=bar"}) + public static interface Intf5MXBean {} + + public static class Intf5Impl + extends NotificationBroadcasterSupport implements Intf5MXBean {} + + private static Object mbeanIntf5 = new Intf5Impl(); + + public static interface Impl1MBean {} + + @NotificationInfo( + types = {"foo", "bar"}, + notificationClass = AttributeChangeNotification.class, + description = @Description( + value = "description", + bundleBaseName = "bundle", + key = "key"), + descriptorFields = {"foo=bar"}) + public static class Impl1 + extends NotificationBroadcasterSupport implements Impl1MBean {} + + private static Object mbeanImpl1 = new Impl1(); + + @NotificationInfo( + types = {"foo", "bar"}, + notificationClass = AttributeChangeNotification.class, + description = @Description( + value = "description", + bundleBaseName = "bundle", + key = "key"), + descriptorFields = {"foo=bar"}) + public static class ParentImpl extends NotificationBroadcasterSupport {} + + public static interface Impl2MBean {} + + public static class Impl2 extends ParentImpl implements Impl2MBean {} + + private static Object mbeanImpl2 = new Impl2(); + + public static interface Impl3MXBean {} + + @NotificationInfo( + types = {"foo", "bar"}, + notificationClass = AttributeChangeNotification.class, + description = @Description( + value = "description", + bundleBaseName = "bundle", + key = "key"), + descriptorFields = {"foo=bar"}) + public static class Impl3 + extends NotificationBroadcasterSupport implements Impl3MXBean {} + + private static Object mbeanImpl3 = new Impl3(); + + public static class Impl4 extends ParentImpl implements Impl3MXBean {} + + private static Object mbeanImpl4 = new Impl4(); + + @MBean + @NotificationInfo( + types = {"foo", "bar"}, + notificationClass = AttributeChangeNotification.class, + description = @Description( + value = "description", + bundleBaseName = "bundle", + key = "key"), + descriptorFields = {"foo=bar"}) + public static class MBean1 extends NotificationBroadcasterSupport {} + + private static Object mbeanMBean1 = new MBean1(); + + @MBean + public static class MBean2 extends ParentImpl {} + + private static Object mbeanMBean2 = new MBean2(); + + // Following disabled until we support it +// @MBean +// @NotificationInfo( +// types = {"foo", "bar"}, +// notificationClass = AttributeChangeNotification.class, +// description = @Description( +// value = "description", +// bundleBaseName = "bundle", +// key = "key"), +// descriptorFields = {"foo=bar"}) +// public static class MBean3 { +// @Resource +// private volatile SendNotification send; +// } +// +// private static Object mbeanMBean3 = new MBean3(); + + @MXBean + @NotificationInfo( + types = {"foo", "bar"}, + notificationClass = AttributeChangeNotification.class, + description = @Description( + value = "description", + bundleBaseName = "bundle", + key = "key"), + descriptorFields = {"foo=bar"}) + public static class MXBean1 extends NotificationBroadcasterSupport {} + + private static Object mbeanMXBean1 = new MXBean1(); + + @MXBean + public static class MXBean2 extends ParentImpl {} + + private static Object mbeanMXBean2 = new MXBean2(); + + public static void main(String[] args) throws Exception { + if (!AnnotatedNotificationInfoTest.class.desiredAssertionStatus()) + throw new Exception("Test must be run with -ea"); + + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + ObjectName on = new ObjectName("a:b=c"); + + Descriptor expectedDescriptor = new ImmutableDescriptor( + "foo=bar", "descriptionResourceBundleBaseName=bundle", + "descriptionResourceKey=key"); + MBeanNotificationInfo expected = new MBeanNotificationInfo( + new String[] {"foo", "bar"}, + AttributeChangeNotification.class.getName(), + "description", + expectedDescriptor); + + System.out.println("Testing MBeans..."); + for (Field mbeanField : + AnnotatedNotificationInfoTest.class.getDeclaredFields()) { + if (!mbeanField.getName().startsWith("mbean")) + continue; + System.out.println("..." + mbeanField.getName()); + Object mbean = mbeanField.get(null); + mbs.registerMBean(mbean, on); + MBeanInfo mbi = mbs.getMBeanInfo(on); + MBeanNotificationInfo[] mbnis = mbi.getNotifications(); + assert mbnis.length == 1 : mbnis.length; + assert mbnis[0].equals(expected) : mbnis[0]; + mbs.unregisterMBean(on); + } + } +} diff --git a/jdk/test/javax/management/Introspector/MBeanDescriptionTest.java b/jdk/test/javax/management/Introspector/MBeanDescriptionTest.java new file mode 100644 index 00000000000..5fc77f9c119 --- /dev/null +++ b/jdk/test/javax/management/Introspector/MBeanDescriptionTest.java @@ -0,0 +1,830 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test %M% %I% + * @bug 6323980 + * @summary Test @Description + * @author Eamonn McManus + */ + +import java.lang.management.ManagementFactory; +import javax.management.Description; +import javax.management.IntrospectionException; +import javax.management.MBean; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanFeatureInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.MBeanServer; +import javax.management.MXBean; +import javax.management.ManagedAttribute; +import javax.management.ManagedOperation; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; +import javax.management.StandardMBean; + +public class MBeanDescriptionTest { + private static String failure; + private static final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + private static final ObjectName name; + static { + try { + name = new ObjectName("a:b=c"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static interface Interface { + @Description("A description") + public String getA(); + + @Description("B description") + public int getB(); + public void setB(int x); + + public boolean isC(); + @Description("C description") + public void setC(boolean x); + + @Description("D description") + public void setD(float x); + + @Description("H description") + public int getH(); + @Description("H description") + public void setH(int x); + + public String getE(); + + public int getF(); + public void setF(int x); + + public void setG(boolean x); + + @Description("opA description") + public int opA( + @Description("p1 description") + int p1, + @Description("p2 description") + int p2); + + public void opB(float x); + } + + @Description("MBean description") + public static interface TestMBean extends Interface {} + + public static class Test implements TestMBean { + @Description("0-arg constructor description") + public Test() {} + + public Test(String why) {} + + @Description("2-arg constructor description") + public Test( + @Description("p1 description") + int x, + @Description("p2 description") + String y) { + } + + public String getA() { + return null; + } + + public int getB() { + return 0; + } + + public void setB(int x) { + } + + public boolean isC() { + return false; + } + + public void setC(boolean x) { + } + + public void setD(float x) { + } + + public String getE() { + return null; + } + + public int getF() { + return 0; + } + + public void setF(int x) { + } + + public void setG(boolean x) { + } + + public int getH() { + return 0; + } + + public void setH(int x) { + } + + public int opA(int p1, int p2) { + return 0; + } + + public void opB(float x) { + } + } + + public static class TestSub extends Test { + @Description("0-arg constructor description") + public TestSub() {} + + public TestSub(String why) {} + + @Description("2-arg constructor description") + public TestSub( + @Description("p1 description") + int x, + @Description("p2 description") + String y) { + } + } + + public static class StandardSub extends StandardMBean implements TestMBean { + @Description("0-arg constructor description") + public StandardSub() { + super(TestMBean.class, false); + } + + public StandardSub(String why) { + super(TestMBean.class, false); + } + + @Description("2-arg constructor description") + public StandardSub( + @Description("p1 description") + int x, + @Description("p2 description") + String y) { + super(TestMBean.class, false); + } + + public String getA() { + return null; + } + + public int getB() { + return 0; + } + + public void setB(int x) { + } + + public boolean isC() { + return false; + } + + public void setC(boolean x) { + } + + public void setD(float x) { + } + + public String getE() { + return null; + } + + public int getF() { + return 0; + } + + public void setF(int x) { + } + + public void setG(boolean x) { + } + + public int opA(int p1, int p2) { + return 0; + } + + public void opB(float x) { + } + + public int getH() { + return 0; + } + + public void setH(int x) { + } + } + + @Description("MBean description") + public static interface TestMXBean extends Interface {} + + public static class TestMXBeanImpl implements TestMXBean { + @Description("0-arg constructor description") + public TestMXBeanImpl() {} + + public TestMXBeanImpl(String why) {} + + @Description("2-arg constructor description") + public TestMXBeanImpl( + @Description("p1 description") + int x, + @Description("p2 description") + String y) { + } + + public String getA() { + return null; + } + + public int getB() { + return 0; + } + + public void setB(int x) { + } + + public boolean isC() { + return false; + } + + public void setC(boolean x) { + } + + public void setD(float x) { + } + + public String getE() { + return null; + } + + public int getF() { + return 0; + } + + public void setF(int x) { + } + + public void setG(boolean x) { + } + + public int opA(int p1, int p2) { + return 0; + } + + public void opB(float x) { + } + + public int getH() { + return 0; + } + + public void setH(int x) { + } + } + + public static class StandardMXSub extends StandardMBean implements TestMXBean { + @Description("0-arg constructor description") + public StandardMXSub() { + super(TestMXBean.class, true); + } + + public StandardMXSub(String why) { + super(TestMXBean.class, true); + } + + @Description("2-arg constructor description") + public StandardMXSub( + @Description("p1 description") + int x, + @Description("p2 description") + String y) { + super(TestMXBean.class, true); + } + + public String getA() { + return null; + } + + public int getB() { + return 0; + } + + public void setB(int x) { + } + + public boolean isC() { + return false; + } + + public void setC(boolean x) { + } + + public void setD(float x) { + } + + public String getE() { + return null; + } + + public int getF() { + return 0; + } + + public void setF(int x) { + } + + public void setG(boolean x) { + } + + public int opA(int p1, int p2) { + return 0; + } + + public void opB(float x) { + } + + public int getH() { + return 0; + } + + public void setH(int x) { + } + } + + @MBean + @Description("MBean description") + public static class AnnotatedMBean { + @Description("0-arg constructor description") + public AnnotatedMBean() {} + + public AnnotatedMBean(String why) {} + + @Description("2-arg constructor description") + public AnnotatedMBean( + @Description("p1 description") + int x, + @Description("p2 description") + String y) {} + + @ManagedAttribute + @Description("A description") + public String getA() { + return null; + } + + @ManagedAttribute + @Description("B description") + public int getB() { + return 0; + } + + @ManagedAttribute + public void setB(int x) { + } + + @ManagedAttribute + public boolean isC() { + return false; + } + + @ManagedAttribute + @Description("C description") + public void setC(boolean x) { + } + + @ManagedAttribute + @Description("D description") + public void setD(float x) { + } + + @ManagedAttribute + public String getE() { + return null; + } + + @ManagedAttribute + public int getF() { + return 0; + } + + @ManagedAttribute + public void setF(int x) { + } + + @ManagedAttribute + public void setG(boolean x) { + } + + @ManagedAttribute + @Description("H description") + public int getH() { + return 0; + } + + @ManagedAttribute + @Description("H description") + public void setH(int x) { + } + + @ManagedOperation + @Description("opA description") + public int opA( + @Description("p1 description") int p1, + @Description("p2 description") int p2) { + return 0; + } + + @ManagedOperation + public void opB(float x) { + } + } + + @MXBean + @Description("MBean description") + public static class AnnotatedMXBean { + @Description("0-arg constructor description") + public AnnotatedMXBean() {} + + public AnnotatedMXBean(String why) {} + + @Description("2-arg constructor description") + public AnnotatedMXBean( + @Description("p1 description") + int x, + @Description("p2 description") + String y) {} + + @ManagedAttribute + @Description("A description") + public String getA() { + return null; + } + + @ManagedAttribute + @Description("B description") + public int getB() { + return 0; + } + + @ManagedAttribute + public void setB(int x) { + } + + @ManagedAttribute + public boolean isC() { + return false; + } + + @ManagedAttribute + @Description("C description") + public void setC(boolean x) { + } + + @ManagedAttribute + @Description("D description") + public void setD(float x) { + } + + @ManagedAttribute + public String getE() { + return null; + } + + @ManagedAttribute + public int getF() { + return 0; + } + + @ManagedAttribute + public void setF(int x) { + } + + @ManagedAttribute + public void setG(boolean x) { + } + + @ManagedAttribute + @Description("H description") + public int getH() { + return 0; + } + + @ManagedAttribute + @Description("H description") + public void setH(int x) { + } + + @ManagedOperation + @Description("opA description") + public int opA( + @Description("p1 description") int p1, + @Description("p2 description") int p2) { + return 0; + } + + @ManagedOperation + public void opB(float x) { + } + } + + // Negative tests follow. + + // Inconsistent descriptions + public static interface BadInterface { + @Description("foo") + public String getFoo(); + @Description("bar") + public void setFoo(String x); + } + + public static interface BadMBean extends BadInterface {} + + public static class Bad implements BadMBean { + public String getFoo() { + return null; + } + + public void setFoo(String x) { + } + } + + public static interface BadMXBean extends BadInterface {} + + public static class BadMXBeanImpl implements BadMXBean { + public String getFoo() { + return null; + } + + public void setFoo(String x) { + } + } + + private static interface Defaults { + public String defaultAttributeDescription(String name); + public String defaultOperationDescription(String name); + public String defaultParameterDescription(int index); + } + + private static class StandardDefaults implements Defaults { + public String defaultAttributeDescription(String name) { + return "Attribute exposed for management"; + } + + public String defaultOperationDescription(String name) { + return "Operation exposed for management"; + } + + public String defaultParameterDescription(int index) { + return ""; + } + } + private static final Defaults standardDefaults = new StandardDefaults(); + + private static class MXBeanDefaults implements Defaults { + public String defaultAttributeDescription(String name) { + return name; + } + + public String defaultOperationDescription(String name) { + return name; + } + + public String defaultParameterDescription(int index) { + return "p" + index; + } + } + private static final Defaults mxbeanDefaults = new MXBeanDefaults(); + + private static class TestCase { + final String name; + final Object mbean; + final Defaults defaults; + TestCase(String name, Object mbean, Defaults defaults) { + this.name = name; + this.mbean = mbean; + this.defaults = defaults; + } + } + + private static class ExceptionTest { + final String name; + final Object mbean; + ExceptionTest(String name, Object mbean) { + this.name = name; + this.mbean = mbean; + } + } + + private static final TestCase[] tests = { + new TestCase("Standard MBean", new Test(), standardDefaults), + new TestCase("Standard MBean subclass", new TestSub(), standardDefaults), + new TestCase("StandardMBean delegating", + new StandardMBean(new Test(), TestMBean.class, false), + standardDefaults), + new TestCase("StandardMBean delegating to subclass", + new StandardMBean(new TestSub(), TestMBean.class, false), + standardDefaults), + new TestCase("StandardMBean subclass", new StandardSub(), standardDefaults), + + new TestCase("MXBean", new TestMXBeanImpl(), mxbeanDefaults), + new TestCase("StandardMBean MXBean delegating", + new StandardMBean(new TestMXBeanImpl(), TestMXBean.class, true), + mxbeanDefaults), + new TestCase("StandardMBean MXBean subclass", + new StandardMXSub(), mxbeanDefaults), + + new TestCase("@MBean", new AnnotatedMBean(), standardDefaults), + new TestCase("@MXBean", new AnnotatedMXBean(), mxbeanDefaults), + new TestCase("StandardMBean @MBean delegating", + new StandardMBean(new AnnotatedMBean(), null, false), + standardDefaults), + new TestCase("StandardMBean @MXBean delegating", + new StandardMBean(new AnnotatedMXBean(), null, true), + mxbeanDefaults), + }; + + private static final ExceptionTest[] exceptionTests = { + new ExceptionTest("Standard MBean with inconsistent get/set", new Bad()), + new ExceptionTest("MXBean with inconsistent get/set", new BadMXBeanImpl()), + }; + + public static void main(String[] args) throws Exception { + System.out.println("=== Testing correct MBeans ==="); + for (TestCase test : tests) { + System.out.println("Testing " + test.name + "..."); + mbs.registerMBean(test.mbean, name); + boolean expectConstructors = + (test.mbean.getClass() != StandardMBean.class); + check(mbs.getMBeanInfo(name), test.defaults, expectConstructors); + mbs.unregisterMBean(name); + } + System.out.println(); + + System.out.println("=== Testing incorrect MBeans ==="); + for (ExceptionTest test : exceptionTests) { + System.out.println("Testing " + test.name); + try { + mbs.registerMBean(test.mbean, name); + fail("Registration succeeded but should not have"); + mbs.unregisterMBean(name); + } catch (NotCompliantMBeanException e) { + // OK + } catch (Exception e) { + fail("Registration failed with wrong exception: " + + "expected NotCompliantMBeanException, got " + + e.getClass().getName()); + } + } + System.out.println(); + + if (failure == null) + System.out.println("TEST PASSED"); + else + throw new Exception("TEST FAILED: " + failure); + } + + private static void check( + MBeanInfo mbi, Defaults defaults, boolean expectConstructors) + throws Exception { + assertEquals("MBean description", mbi.getDescription()); + + // These attributes have descriptions + for (String attr : new String[] {"A", "B", "C", "D", "H"}) { + MBeanAttributeInfo mbai = getAttributeInfo(mbi, attr); + assertEquals(attr + " description", mbai.getDescription()); + } + + // These attributes don't have descriptions + for (String attr : new String[] {"E", "F", "G"}) { + // If we ever change the default description, we'll need to change + // this test accordingly. + MBeanAttributeInfo mbai = getAttributeInfo(mbi, attr); + assertEquals( + defaults.defaultAttributeDescription(attr), mbai.getDescription()); + } + + // This operation has a description, as do its parameters + MBeanOperationInfo opA = getOperationInfo(mbi, "opA"); + assertEquals("opA description", opA.getDescription()); + checkSignature(opA.getSignature()); + + // This operation has the default description, as does its parameter + MBeanOperationInfo opB = getOperationInfo(mbi, "opB"); + assertEquals(defaults.defaultOperationDescription("opB"), opB.getDescription()); + MBeanParameterInfo opB0 = opB.getSignature()[0]; + assertEquals(defaults.defaultParameterDescription(0), opB0.getDescription()); + + if (expectConstructors) { + // The 0-arg and 2-arg constructors have descriptions + MBeanConstructorInfo con0 = getConstructorInfo(mbi, 0); + assertEquals("0-arg constructor description", con0.getDescription()); + MBeanConstructorInfo con2 = getConstructorInfo(mbi, 2); + assertEquals("2-arg constructor description", con2.getDescription()); + checkSignature(con2.getSignature()); + + // The 1-arg constructor does not have a description. + // The default description for constructors and their + // parameters is the same for all types of MBean. + MBeanConstructorInfo con1 = getConstructorInfo(mbi, 1); + assertEquals("Public constructor of the MBean", con1.getDescription()); + assertEquals("", con1.getSignature()[0].getDescription()); + } + } + + private static void checkSignature(MBeanParameterInfo[] params) { + for (int i = 0; i < params.length; i++) { + MBeanParameterInfo mbpi = params[i]; + assertEquals("p" + (i+1) + " description", mbpi.getDescription()); + } + } + + private static MBeanAttributeInfo getAttributeInfo(MBeanInfo mbi, String attr) + throws Exception { + return getFeatureInfo(mbi.getAttributes(), attr); + } + + private static MBeanOperationInfo getOperationInfo(MBeanInfo mbi, String op) + throws Exception { + return getFeatureInfo(mbi.getOperations(), op); + } + + private static MBeanConstructorInfo getConstructorInfo(MBeanInfo mbi, int nparams) + throws Exception { + for (MBeanConstructorInfo mbci : mbi.getConstructors()) { + if (mbci.getSignature().length == nparams) + return mbci; + } + throw new Exception("Constructor not found: " + nparams); + } + + private static T getFeatureInfo( + T[] features, String name) throws Exception { + for (T feature : features) { + if (feature.getName().equals(name)) + return feature; + } + throw new Exception("Feature not found: " + name); + } + + private static void assertEquals(Object expected, Object actual) { + if (!expected.equals(actual)) + fail("Expected " + string(expected) + ", got " + string(actual)); + } + + private static String string(Object x) { + if (x instanceof String) + return quote((String) x); + else + return String.valueOf(x); + } + + private static String quote(String s) { + return '"' + s.replace("\\", "\\\\").replace("\"", "\\\"") + '"'; + } + + private static void fail(String why) { + StackTraceElement[] stack = new Throwable().getStackTrace(); + int n = 0; + for (StackTraceElement elmt : stack) { + String method = elmt.getMethodName(); + if (method.equals("fail") || method.equals("assertEquals") || + method.equals("checkSignature")) + continue; + n = elmt.getLineNumber(); + break; + } + System.out.println("FAILED: " + why + " (line " + n + ")"); + failure = why; + } +} diff --git a/jdk/test/javax/management/Introspector/ParameterNameTest.java b/jdk/test/javax/management/Introspector/ParameterNameTest.java new file mode 100644 index 00000000000..fada6cccb17 --- /dev/null +++ b/jdk/test/javax/management/Introspector/ParameterNameTest.java @@ -0,0 +1,116 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test %M% %I% + * @bug 6323980 + * @summary Test that parameter names can be specified with @Name. + * @author Eamonn McManus + */ + +import javax.management.MBean; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MXBean; +import javax.management.ObjectName; + +import annot.Name; +import javax.management.ManagedOperation; + +public class ParameterNameTest { + public static interface NoddyMBean { + public int add(int x, @Name("y") int y); + } + + public static class Noddy implements NoddyMBean { + public int add(int x, int y) { + return x + y; + } + } + + public static interface NoddyMXBean { + public int add(int x, @Name("y") int y); + } + + public static class NoddyImpl implements NoddyMXBean { + public int add(int x, int y) { + return x + y; + } + } + + @MBean + public static class NoddyAnnot { + @ManagedOperation + public int add(int x, @Name("y") int y) { + return x + y; + } + } + + @MXBean + public static class NoddyAnnotMX { + @ManagedOperation + public int add(int x, @Name("y") int y) { + return x + y; + } + } + + private static final Object[] mbeans = { + new Noddy(), new NoddyImpl(), new NoddyAnnot(), new NoddyAnnotMX(), + }; + + public static void main(String[] args) throws Exception { + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + ObjectName name = new ObjectName("a:b=c"); + for (Object mbean : mbeans) { + System.out.println("Testing " + mbean.getClass().getName()); + mbs.registerMBean(mbean, name); + MBeanInfo mbi = mbs.getMBeanInfo(name); + MBeanOperationInfo[] mbois = mbi.getOperations(); + assertEquals(1, mbois.length); + MBeanParameterInfo[] mbpis = mbois[0].getSignature(); + assertEquals(2, mbpis.length); + boolean mx = Boolean.parseBoolean( + (String) mbi.getDescriptor().getFieldValue("mxbean")); + assertEquals(mx ? "p0" : "p1", mbpis[0].getName()); + assertEquals("y", mbpis[1].getName()); + mbs.unregisterMBean(name); + } + System.out.println("TEST PASSED"); + } + + private static void assertEquals(Object expect, Object actual) + throws Exception { + boolean eq; + if (expect == null) + eq = (actual == null); + else + eq = expect.equals(actual); + if (!eq) { + throw new Exception( + "TEST FAILED: expected " + expect + ", found " + actual); + } + } +} diff --git a/jdk/test/javax/management/Introspector/ResourceInjectionTest.java b/jdk/test/javax/management/Introspector/ResourceInjectionTest.java new file mode 100644 index 00000000000..ad45ccc029f --- /dev/null +++ b/jdk/test/javax/management/Introspector/ResourceInjectionTest.java @@ -0,0 +1,656 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test %M% %I% + * @bug 6323980 + * @summary Test resource injection via @Resource + * @author Eamonn McManus + * @run main/othervm -ea ResourceInjectionTest + */ + +import java.io.File; +import java.io.PrintWriter; +import java.io.Serializable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import javax.annotation.Resource; +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.DynamicMBean; +import javax.management.InstanceNotFoundException; +import javax.management.MBean; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MXBean; +import javax.management.MalformedObjectNameException; +import javax.management.ManagedAttribute; +import javax.management.ManagedOperation; +import javax.management.NotCompliantMBeanException; +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.SendNotification; +import javax.management.StandardEmitterMBean; +import javax.management.StandardMBean; +import javax.management.openmbean.MXBeanMappingFactory; + +public class ResourceInjectionTest { + private static MBeanServer mbs; + private static final ObjectName objectName; + static { + try { + objectName = new ObjectName("test:type=Test"); + } catch (MalformedObjectNameException e) { + throw new RuntimeException(e); + } + } + + /* This is somewhat nasty. In the current state of affairs, a + * StandardEmitterMBean can only get the + * MBeanServer to rewrite the source of a Notification from + * the originating object's reference to its ObjectName IF + * StandardEmitterMBean.getResource() returns a reference to the + * wrapped object. By default it doesn't, and you need to specify + * the option below to make it do so. We may hope that this is + * obscure enough for users to run into it rarely if ever. + */ + private static final StandardMBean.Options withWrappedVisible; + private static final StandardMBean.Options withWrappedVisibleMX; + static { + withWrappedVisible = new StandardMBean.Options(); + withWrappedVisible.setWrappedObjectVisible(true); + withWrappedVisibleMX = withWrappedVisible.clone(); + withWrappedVisibleMX.setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT); + } + + @Retention(RetentionPolicy.RUNTIME) + private static @interface ExpectException { + Class value(); + } + + public static void main(String[] args) throws Exception { + if (!ResourceInjectionTest.class.desiredAssertionStatus()) + throw new Exception("Test must be run with -ea"); + + File policyFile = File.createTempFile("jmxperms", ".policy"); + policyFile.deleteOnExit(); + PrintWriter pw = new PrintWriter(policyFile); + pw.println("grant {"); + pw.println(" permission javax.management.MBeanPermission \"*\", \"*\";"); + pw.println(" permission javax.management.MBeanServerPermission \"*\";"); + pw.println(" permission javax.management.MBeanTrustPermission \"*\";"); + pw.println("};"); + pw.close(); + + System.setProperty("java.security.policy", policyFile.getAbsolutePath()); + System.setSecurityManager(new SecurityManager()); + + String failure = null; + + for (Method m : ResourceInjectionTest.class.getDeclaredMethods()) { + if (Modifier.isStatic(m.getModifiers()) && + m.getName().startsWith("test") && + m.getParameterTypes().length == 0) { + ExpectException expexc = m.getAnnotation(ExpectException.class); + mbs = MBeanServerFactory.newMBeanServer(); + try { + m.invoke(null); + if (expexc != null) { + failure = + m.getName() + " did not got expected exception " + + expexc.value().getName(); + System.out.println(failure); + } else + System.out.println(m.getName() + " OK"); + } catch (InvocationTargetException ite) { + Throwable t = ite.getCause(); + String prob = null; + if (expexc != null) { + if (expexc.value().isInstance(t)) { + System.out.println(m.getName() + " OK (got expected " + + expexc.value().getName() + ")"); + } else + prob = "got wrong exception"; + } else + prob = "got exception"; + if (prob != null) { + failure = m.getName() + ": " + prob + " " + + t.getClass().getName(); + System.out.println(failure); + t.printStackTrace(System.out); + } + } + } + } + if (failure == null) + System.out.println("TEST PASSED"); + else + throw new Exception("TEST FAILED: " + failure); + } + + private static interface Send { + public void send(); + } + + // Test @Resource in MBean defined by annotations + + @MBean + public static class Annotated { + @Resource + private volatile MBeanServer mbeanServer; + @Resource + private volatile ObjectName myName; + + @ManagedAttribute + public ObjectName getMyName() { + return myName; + } + + @ManagedOperation + public void unregisterSelf() + throws InstanceNotFoundException, MBeanRegistrationException { + mbeanServer.unregisterMBean(myName); + } + } + + private static void testAnnotated() throws Exception { + testMBean(new Annotated()); + } + + private static void testAnnotatedWrapped() throws Exception { + testMBean(new StandardMBean(new Annotated(), null)); + } + + @MBean + public static class AnnotatedSend extends Annotated implements Send { + @Resource + private volatile SendNotification sender; + + @ManagedOperation + public void send() { + sender.sendNotification(new Notification("type", this, 0L)); + } + } + + private static void testAnnotatedSend() throws Exception { + testMBean(new AnnotatedSend()); + } + + private static void testAnnotatedSendWrapped() throws Exception { + testMBean(new StandardEmitterMBean( + new AnnotatedSend(), null, withWrappedVisible, null)); + } + + // Test @Resource in MXBean defined by annotations + + @MXBean + public static class AnnotatedMX { + @Resource + private volatile MBeanServer mbeanServer; + @Resource + private volatile ObjectName myName; + + @ManagedAttribute + public ObjectName getMyName() { + return myName; + } + + @ManagedOperation + public void unregisterSelf() + throws InstanceNotFoundException, MBeanRegistrationException { + mbeanServer.unregisterMBean(myName); + } + } + + private static void testAnnotatedMX() throws Exception { + testMBean(new AnnotatedMX()); + } + + private static void testAnnotatedMXWrapped() throws Exception { + testMBean(new StandardMBean(new AnnotatedMX(), null, true)); + } + + public static class AnnotatedMXSend extends AnnotatedMX implements Send { + @Resource + private volatile SendNotification sender; + + @ManagedOperation + public void send() { + sender.sendNotification(new Notification("type", this, 0L)); + } + } + + private static void testAnnotatedMXSend() throws Exception { + testMBean(new AnnotatedMXSend()); + } + + private static void testAnnotatedMXSendWrapped() throws Exception { + testMBean(new StandardEmitterMBean( + new AnnotatedMXSend(), null, withWrappedVisibleMX, null)); + } + + // Test @Resource in Standard MBean + + public static interface SimpleStandardMBean { + public ObjectName getMyName(); + public void unregisterSelf() throws Exception; + } + + public static class SimpleStandard implements SimpleStandardMBean { + @Resource(type = MBeanServer.class) + private volatile Object mbeanServer; + @Resource(type = ObjectName.class) + private volatile Object myName; + + public ObjectName getMyName() { + return (ObjectName) myName; + } + + public void unregisterSelf() throws Exception { + ((MBeanServer) mbeanServer).unregisterMBean(getMyName()); + } + } + + private static void testStandard() throws Exception { + testMBean(new SimpleStandard()); + } + + private static void testStandardWrapped() throws Exception { + testMBean(new StandardMBean(new SimpleStandard(), SimpleStandardMBean.class)); + } + + public static interface SimpleStandardSendMBean extends SimpleStandardMBean { + public void send(); + } + + public static class SimpleStandardSend + extends SimpleStandard implements SimpleStandardSendMBean { + @Resource(type = SendNotification.class) + private volatile Object sender; + + public void send() { + ((SendNotification) sender).sendNotification( + new Notification("type", this, 0L)); + } + } + + private static void testStandardSend() throws Exception { + testMBean(new SimpleStandardSend()); + } + + private static void testStandardSendWrapped() throws Exception { + testMBean(new StandardEmitterMBean( + new SimpleStandardSend(), SimpleStandardSendMBean.class, + withWrappedVisible, null)); + } + + // Test @Resource in MXBean + + public static interface SimpleMXBean { + public ObjectName getMyName(); + public void unregisterSelf() throws Exception; + } + + public static class SimpleMX implements SimpleMXBean { + @Resource(type = MBeanServer.class) + private volatile Object mbeanServer; + @Resource(type = ObjectName.class) + private volatile Object myName; + + public ObjectName getMyName() { + return (ObjectName) myName; + } + + public void unregisterSelf() throws Exception { + ((MBeanServer) mbeanServer).unregisterMBean(getMyName()); + } + } + + private static void testMX() throws Exception { + testMBean(new SimpleMX()); + } + + private static void testMXWrapped() throws Exception { + testMBean(new StandardMBean(new SimpleMX(), SimpleMXBean.class, true)); + } + + public static interface SimpleMXBeanSend extends SimpleMXBean { + public void send(); + } + + public MBeanServer getMbs() { + return mbs; + } + + public static class SimpleMXSend extends SimpleMX implements SimpleMXBeanSend { + @Resource(type = SendNotification.class) + private volatile Object sender; + + public void send() { + ((SendNotification) sender).sendNotification( + new Notification("type", this, 0L)); + } + } + + private static void testMXSend() throws Exception { + testMBean(new SimpleMXSend()); + } + + private static void testMXSendWrapped() throws Exception { + testMBean(new StandardEmitterMBean( + new SimpleMXSend(), SimpleMXBeanSend.class, + withWrappedVisibleMX, null)); + } + + // Test @Resource in Dynamic MBean + + private static class SimpleDynamic implements DynamicMBean { + private MBeanServer mbeanServer; + private ObjectName myName; + + @Resource + private synchronized void setMBeanServer(MBeanServer mbs) { + mbeanServer = mbs; + } + + @Resource(type = ObjectName.class) + private synchronized void setObjectName(Serializable name) { + myName = (ObjectName) name; + } + + public synchronized Object getAttribute(String attribute) + throws AttributeNotFoundException { + if (attribute.equals("MyName")) + return myName; + throw new AttributeNotFoundException(attribute); + } + + public void setAttribute(Attribute attribute) + throws AttributeNotFoundException { + throw new AttributeNotFoundException(attribute.getName()); + } + + public synchronized AttributeList getAttributes(String[] attributes) { + AttributeList list = new AttributeList(); + for (String name : attributes) { + if (name.equals("MyName")) + list.add(new Attribute("MyName", myName)); + } + return list; + } + + public AttributeList setAttributes(AttributeList attributes) { + return new AttributeList(); + } + + public synchronized Object invoke( + String actionName, Object[] params, String[] signature) + throws MBeanException, ReflectionException { + if (actionName.equals("unregisterSelf") && + (params == null || params.length == 0) && + (signature == null || signature.length == 0)) { + try { + mbeanServer.unregisterMBean(myName); + return null; + } catch (Exception x) { + throw new MBeanException(x); + } + } else { + Exception x = new NoSuchMethodException( + actionName + Arrays.toString(signature)); + throw new MBeanException(x); + } + } + + public MBeanInfo getMBeanInfo() { + DynamicMBean mbean = new StandardMBean( + new SimpleStandard(), SimpleStandardMBean.class, false); + return mbean.getMBeanInfo(); + } + } + + private static void testDynamic() throws Exception { + testMBean(new SimpleDynamic()); + } + + private static class SimpleDynamicSend extends SimpleDynamic { + private SendNotification sender; + + @Resource + private synchronized void setSender(SendNotification sender) { + this.sender = sender; + } + + @Override + public synchronized Object invoke( + String actionName, Object[] params, String[] signature) + throws MBeanException, ReflectionException { + if (actionName.equals("send")) { + sender.sendNotification(new Notification("type", this, 0L)); + return null; + } else + return super.invoke(actionName, params, signature); + } + } + + private static void testDynamicSend() throws Exception { + testMBean(new SimpleDynamicSend()); + } + + // Test that @Resource classes don't have to be public + // They can even be defined within methods! + // But you can't have any @ManagedAttributes or @ManagedOperations + // in such MBeans so their utility is limited. + + private static void testNonPublic() throws Exception { + @MBean + class NonPublic { + @Resource + ObjectName myName; + } + assert !Modifier.isPublic(NonPublic.class.getModifiers()); + NonPublic mbean = new NonPublic(); + mbs.registerMBean(mbean, objectName); + assert objectName.equals(mbean.myName); + } + + // Test inheritance and multiple injections of the same value + + private static class ManyResources extends AnnotatedSend { + @Resource + private volatile ObjectName myName; // same name as in parent! + @Resource(type=ObjectName.class) + private volatile Object myOtherName; + private volatile ObjectName myThirdName; + private volatile ObjectName myFourthName; + private volatile int methodCalls; + @Resource + private volatile SendNotification send1; + @Resource(type = SendNotification.class) + private volatile Object send2; + + @Resource + void setMyName(ObjectName name) { + myThirdName = name; + methodCalls++; + } + + @Resource(type=ObjectName.class) + private void setMyNameAgain(ObjectName name) { + myFourthName = name; + methodCalls++; + } + + void check() { + assert objectName.equals(myName) : myName; + for (ObjectName name : new ObjectName[] { + (ObjectName)myOtherName, myThirdName, myFourthName + }) { + assert myName == name : name; + } + assert methodCalls == 2 : methodCalls; + assert send1 != null && send2 == send1; + } + } + + private static void testManyResources() throws Exception { + ManyResources mr = new ManyResources(); + testMBean(mr); + mr.check(); + } + + // Test that method override doesn't lead to multiple calls of the same method + + private static class ManyResourcesSub extends ManyResources { + private boolean called; + + @Override + @Resource + void setMyName(ObjectName name) { + super.setMyName(name); + called = true; + } + + void check2() { + assert called; + } + } + + private static void testOverride() throws Exception { + ManyResourcesSub mrs = new ManyResourcesSub(); + testMBean(mrs); + mrs.check(); + mrs.check2(); + } + + // Test that @Resource is illegal on static fields + + @MBean + public static class StaticResource { + @Resource + private static ObjectName name; + } + + @ExpectException(NotCompliantMBeanException.class) + private static void testStaticResource() throws Exception { + testMBean(new StaticResource()); + } + + // Test that @Resource is illegal on static methods + + @MBean + public static class StaticResourceMethod { + @Resource + private static void setObjectName(ObjectName name) {} + } + + @ExpectException(NotCompliantMBeanException.class) + private static void testStaticResourceMethod() throws Exception { + testMBean(new StaticResourceMethod()); + } + + // Test that @Resource is illegal on methods that don't return void + + @MBean + public static class NonVoidMethod { + @Resource + private String setObjectName(ObjectName name) { + return "oops"; + } + } + + @ExpectException(NotCompliantMBeanException.class) + private static void testNonVoidMethod() throws Exception { + testMBean(new NonVoidMethod()); + } + + // Test that @Resource is illegal on methods with no arguments + + @MBean + public static class NoArgMethod { + @Resource(type=ObjectName.class) + private void setObjectName() {} + } + + @ExpectException(NotCompliantMBeanException.class) + private static void testNoArgMethod() throws Exception { + testMBean(new NoArgMethod()); + } + + // Test that @Resource is illegal on methods with more than one argument + + @MBean + public static class MultiArgMethod { + @Resource + private void setObjectName(ObjectName name, String what) {} + } + + @ExpectException(NotCompliantMBeanException.class) + private static void testMultiArgMethod() throws Exception { + testMBean(new MultiArgMethod()); + } + + private static class CountListener implements NotificationListener { + volatile int count; + public void handleNotification(Notification notification, Object handback) { + count++; + } + } + + private static void testMBean(Object mbean) throws Exception { + mbs.registerMBean(mbean, objectName); + + final ObjectName name = (ObjectName) mbs.getAttribute(objectName, "MyName"); + assert objectName.equals(name) : name; + + if (mbean instanceof Send || mbean instanceof NotificationEmitter) { + assert mbs.isInstanceOf(name, NotificationEmitter.class.getName()); + CountListener countL = new CountListener(); + mbs.addNotificationListener(name, countL, null, null); + NotificationListener checkSource = new NotificationListener() { + public void handleNotification(Notification n, Object h) { + assert n.getSource().equals(name) : n.getSource(); + } + }; + mbs.addNotificationListener(name, checkSource, null, null); + mbs.invoke(objectName, "send", null, null); + assert countL.count == 1; + mbs.removeNotificationListener(name, checkSource); + mbs.removeNotificationListener(name, countL, null, null); + } + + mbs.invoke(objectName, "unregisterSelf", null, null); + assert !mbs.isRegistered(objectName); + } +} diff --git a/jdk/test/javax/management/Introspector/annot/Name.java b/jdk/test/javax/management/Introspector/annot/Name.java new file mode 100644 index 00000000000..790e641552e --- /dev/null +++ b/jdk/test/javax/management/Introspector/annot/Name.java @@ -0,0 +1,32 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package annot; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Name { + String value(); +} From 4c95421013701dca4e79a34f5ef5de7883d71c5b Mon Sep 17 00:00:00 2001 From: Jim Holmlund Date: Wed, 9 Jul 2008 13:43:26 -0700 Subject: [PATCH 05/21] 6700889: Thread resume invalidates all stack frames, even from other threads 6701700: MonitorInfo objects aren't invalidated when the owning thread is resumed Don't inform ThreadListeners for thread T1 when some other thread is resumed, and MonitoryIfoImpl must add itself as a ThreadListener Reviewed-by: dcubed --- .../com/sun/tools/jdi/MonitorInfoImpl.java | 3 +- .../sun/tools/jdi/ThreadReferenceImpl.java | 253 +++++++++--------- .../classes/com/sun/tools/jdi/VMAction.java | 12 + .../classes/com/sun/tools/jdi/VMState.java | 15 +- .../com/sun/tools/jdi/VirtualMachineImpl.java | 3 +- jdk/test/com/sun/jdi/MonitorFrameInfo.java | 32 ++- jdk/test/com/sun/jdi/ResumeOneThreadTest.java | 233 ++++++++++++++++ 7 files changed, 416 insertions(+), 135 deletions(-) create mode 100644 jdk/test/com/sun/jdi/ResumeOneThreadTest.java diff --git a/jdk/src/share/classes/com/sun/tools/jdi/MonitorInfoImpl.java b/jdk/src/share/classes/com/sun/tools/jdi/MonitorInfoImpl.java index a9a22abc076..ea2f49da5aa 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/MonitorInfoImpl.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/MonitorInfoImpl.java @@ -40,11 +40,12 @@ public class MonitorInfoImpl extends MirrorImpl int stack_depth; MonitorInfoImpl(VirtualMachine vm, ObjectReference mon, - ThreadReference thread, int dpth) { + ThreadReferenceImpl thread, int dpth) { super(vm); this.monitor = mon; this.thread = thread; this.stack_depth = dpth; + thread.addListener(this); } diff --git a/jdk/src/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java b/jdk/src/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java index e70c2ef250c..7af01561e9b 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java @@ -35,12 +35,34 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl static final int SUSPEND_STATUS_SUSPENDED = 0x1; static final int SUSPEND_STATUS_BREAK = 0x2; - private ThreadGroupReference threadGroup; private int suspendedZombieCount = 0; - // This is cached only while the VM is suspended - private static class Cache extends ObjectReferenceImpl.Cache { - String name = null; + /* + * Some objects can only be created while a thread is suspended and are valid + * 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; List frames = null; int framesStart = -1; @@ -52,6 +74,17 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl 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() { return new Cache(); } @@ -59,8 +92,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl // Listeners - synchronized on vm.state() private List> listeners = new ArrayList>(); + ThreadReferenceImpl(VirtualMachine aVm, long aRef) { super(aVm,aRef); + resetLocalCache(); vm.state().addListener(this); } @@ -72,10 +107,24 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl * VMListener implementation */ public boolean vmNotSuspended(VMAction action) { - synchronized (vm.state()) { - processThreadAction(new ThreadAction(this, - ThreadAction.THREAD_RESUMABLE)); + if (action.resumingThread() == null) { + // all threads are being resumed + 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); } @@ -191,23 +240,19 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl } private JDWP.ThreadReference.Status jdwpStatus() { - JDWP.ThreadReference.Status status = null; + JDWP.ThreadReference.Status myStatus = localCache.status; try { - Cache local = (Cache)getCache(); - - if (local != null) { - status = local.status; - } - if (status == null) { - status = JDWP.ThreadReference.Status.process(vm, this); - if (local != null) { - local.status = status; + if (myStatus == null) { + myStatus = JDWP.ThreadReference.Status.process(vm, this); + if ((myStatus.suspendStatus & SUSPEND_STATUS_SUSPENDED) != 0) { + // thread is suspended, we can cache the status. + localCache.status = myStatus; } } - } catch (JDWPException exc) { + } catch (JDWPException exc) { throw exc.toJDIException(); } - return status; + return myStatus; } public int status() { @@ -245,8 +290,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl public ThreadGroupReference threadGroup() { /* - * Thread group can't change, so it's cached more conventionally - * than other things in this class. + * Thread group can't change, so it's cached once and for all. */ if (threadGroup == null) { try { @@ -260,19 +304,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl } public int frameCount() throws IncompatibleThreadStateException { - int frameCount = -1; try { - Cache local = (Cache)getCache(); - - if (local != null) { - frameCount = local.frameCount; - } - if (frameCount == -1) { - frameCount = JDWP.ThreadReference.FrameCount + if (localCache.frameCount == -1) { + localCache.frameCount = JDWP.ThreadReference.FrameCount .process(vm, this).frameCount; - if (local != null) { - local.frameCount = frameCount; - } } } catch (JDWPException exc) { switch (exc.errorCode()) { @@ -283,7 +318,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl throw exc.toJDIException(); } } - return frameCount; + return localCache.frameCount; } public List frames() throws IncompatibleThreadStateException { @@ -297,23 +332,25 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl /** * 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, - int start, int length, List frames) { - if (start < local.framesStart) { + private boolean isSubrange(LocalCache localCache, + int start, int length) { + if (start < localCache.framesStart) { return false; } if (length == -1) { - return (local.framesLength == -1); + return (localCache.framesLength == -1); } - if (local.framesLength == -1) { - if ((start + length) > (local.framesStart + frames.size())) { + if (localCache.framesLength == -1) { + if ((start + length) > (localCache.framesStart + + localCache.frames.size())) { throw new IndexOutOfBoundsException(); } return true; } - return ((start + length) <= (local.framesStart + local.framesLength)); + return ((start + length) <= (localCache.framesStart + localCache.framesLength)); } public List frames(int start, int length) @@ -329,51 +366,42 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl * Private version of frames() allows "-1" to specify all * remaining frames. */ - private List privateFrames(int start, int length) + synchronized private List privateFrames(int start, int length) throws IncompatibleThreadStateException { - List frames = null; - try { - Cache local = (Cache)getCache(); - if (local != null) { - frames = local.frames; - } - if (frames == null || !isSubrange(local, start, length, frames)) { + // Lock must be held while creating stack frames so if that two threads + // do this at the same time, one won't clobber the subset created by the other. + + try { + if (localCache.frames == null || !isSubrange(localCache, start, length)) { JDWP.ThreadReference.Frames.Frame[] jdwpFrames = JDWP.ThreadReference.Frames. - process(vm, this, start, length).frames; + process(vm, this, start, length).frames; int count = jdwpFrames.length; - frames = new ArrayList(count); + localCache.frames = new ArrayList(count); - // Lock must be held while creating stack frames. - // so that a resume will not resume a partially - // created stack. - synchronized (vm.state()) { - for (int i = 0; i ownedMonitors() throws IncompatibleThreadStateException { - List monitors = null; try { - Cache local = (Cache)getCache(); - - if (local != null) { - monitors = local.ownedMonitors; - } - if (monitors == null) { - monitors = Arrays.asList( + if (localCache.ownedMonitors == null) { + localCache.ownedMonitors = Arrays.asList( (ObjectReference[])JDWP.ThreadReference.OwnedMonitors. process(vm, this).owned); - if (local != null) { - local.ownedMonitors = monitors; - if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { - vm.printTrace(description() + - " temporarily caching owned monitors"+ - " (count = " + monitors.size() + ")"); - } + if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { + vm.printTrace(description() + + " temporarily caching owned monitors"+ + " (count = " + localCache.ownedMonitors.size() + ")"); } } } catch (JDWPException exc) { @@ -417,29 +435,22 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl throw exc.toJDIException(); } } - return monitors; + return localCache.ownedMonitors; } public ObjectReference currentContendedMonitor() throws IncompatibleThreadStateException { - ObjectReference monitor = null; try { - Cache local = (Cache)getCache(); - - if (local != null && local.triedCurrentContended) { - monitor = local.contendedMonitor; - } else { - monitor = JDWP.ThreadReference.CurrentContendedMonitor. + if (localCache.contendedMonitor == null && + !localCache.triedCurrentContended) { + localCache.contendedMonitor = JDWP.ThreadReference.CurrentContendedMonitor. process(vm, this).monitor; - if (local != null) { - local.triedCurrentContended = true; - local.contendedMonitor = monitor; - if ((monitor != null) && - ((vm.traceFlags & vm.TRACE_OBJREFS) != 0)) { - vm.printTrace(description() + - " temporarily caching contended monitor"+ - " (id = " + monitor.uniqueID() + ")"); - } + localCache.triedCurrentContended = true; + if ((localCache.contendedMonitor != null) && + ((vm.traceFlags & vm.TRACE_OBJREFS) != 0)) { + vm.printTrace(description() + + " temporarily caching contended monitor"+ + " (id = " + localCache.contendedMonitor.uniqueID() + ")"); } } } catch (JDWPException exc) { @@ -450,40 +461,31 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl throw exc.toJDIException(); } } - return monitor; + return localCache.contendedMonitor; } public List ownedMonitorsAndFrames() throws IncompatibleThreadStateException { - List monitors = null; try { - Cache local = (Cache)getCache(); - - if (local != null) { - monitors = local.ownedMonitorsInfo; - } - if (monitors == null) { + if (localCache.ownedMonitorsInfo == null) { JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor[] minfo; minfo = JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.process(vm, this).owned; - monitors = new ArrayList(minfo.length); + localCache.ownedMonitorsInfo = new ArrayList(minfo.length); for (int i=0; i < minfo.length; i++) { JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor mi = minfo[i]; MonitorInfo mon = new MonitorInfoImpl(vm, minfo[i].monitor, this, minfo[i].stack_depth); - monitors.add(mon); + localCache.ownedMonitorsInfo.add(mon); } - if (local != null) { - local.ownedMonitorsInfo = monitors; - if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { - vm.printTrace(description() + - " temporarily caching owned monitors"+ - " (count = " + monitors.size() + ")"); + if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { + vm.printTrace(description() + + " temporarily caching owned monitors"+ + " (count = " + localCache.ownedMonitorsInfo.size() + ")"); } } - } } catch (JDWPException exc) { switch (exc.errorCode()) { case JDWP.Error.THREAD_NOT_SUSPENDED: @@ -493,7 +495,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl throw exc.toJDIException(); } } - return monitors; + return localCache.ownedMonitorsInfo; } public void popFrames(StackFrame frame) throws IncompatibleThreadStateException { @@ -511,7 +513,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl } public void forceEarlyReturn(Value returnValue) throws InvalidTypeException, - ClassNotLoadedException, + ClassNotLoadedException, IncompatibleThreadStateException { if (!vm.canForceEarlyReturn()) { throw new UnsupportedOperationException( @@ -604,6 +606,9 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl iter.remove(); } } + + // Discard our local cache + resetLocalCache(); } } } diff --git a/jdk/src/share/classes/com/sun/tools/jdi/VMAction.java b/jdk/src/share/classes/com/sun/tools/jdi/VMAction.java index fd4f6d37ba9..7a5628e0acd 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/VMAction.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/VMAction.java @@ -38,10 +38,18 @@ class VMAction extends EventObject { static final int VM_NOT_SUSPENDED = 2; int id; + ThreadReference resumingThread; 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); this.id = id; + this.resumingThread = resumingThread; } VirtualMachine vm() { return (VirtualMachine)getSource(); @@ -49,4 +57,8 @@ class VMAction extends EventObject { int id() { return id; } + + ThreadReference resumingThread() { + return resumingThread; + } } diff --git a/jdk/src/share/classes/com/sun/tools/jdi/VMState.java b/jdk/src/share/classes/com/sun/tools/jdi/VMState.java index 369ce472548..789e5acdb73 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/VMState.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/VMState.java @@ -116,16 +116,25 @@ class VMState { } /** - * Tell listeners to invalidate suspend-sensitive caches. + * All threads are resuming */ - synchronized void thaw() { + void thaw() { + thaw(null); + } + + /** + * Tell listeners to invalidate suspend-sensitive caches. + * If resumingThread != null, then only that thread is being + * resumed. + */ + synchronized void thaw(ThreadReference resumingThread) { if (cache != null) { if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { vm.printTrace("Clearing VM suspended cache"); } disableCache(); } - processVMAction(new VMAction(vm, VMAction.VM_NOT_SUSPENDED)); + processVMAction(new VMAction(vm, resumingThread, VMAction.VM_NOT_SUSPENDED)); } private synchronized void processVMAction(VMAction action) { diff --git a/jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineImpl.java b/jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineImpl.java index e6dd311e152..592d248981b 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineImpl.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineImpl.java @@ -146,8 +146,9 @@ class VirtualMachineImpl extends MirrorImpl public boolean threadResumable(ThreadAction action) { /* * 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; } diff --git a/jdk/test/com/sun/jdi/MonitorFrameInfo.java b/jdk/test/com/sun/jdi/MonitorFrameInfo.java index e93fa3390d2..8f7bfce34d2 100644 --- a/jdk/test/com/sun/jdi/MonitorFrameInfo.java +++ b/jdk/test/com/sun/jdi/MonitorFrameInfo.java @@ -25,7 +25,8 @@ * @test * @bug 6230699 * @summary Test ThreadReference.ownedMonitorsAndFrames() - * + * @bug 6701700 + * @summary MonitorInfo objects aren't invalidated when the owning thread is resumed * @author Swamy Venkataramanappa * * @run build TestScaffold VMConnection TargetListener TargetAdapter @@ -100,15 +101,15 @@ public class MonitorFrameInfo extends TestScaffold { if (!mainThread.frame(0).location().method().name() .equals("foo3")) { - failure("frame failed"); + failure("FAILED: frame failed"); } if (mainThread.frames().size() != (initialSize + 3)) { - failure("frames size failed"); + failure("FAILED: frames size failed"); } 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. @@ -119,13 +120,32 @@ public class MonitorFrameInfo extends TestScaffold { if (monitors.size() != expectedCount) { failure("monitors count is not equal to expected count"); } + MonitorInfo mon = null; 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()); 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 { System.out.println("can not get monitors frame info"); } diff --git a/jdk/test/com/sun/jdi/ResumeOneThreadTest.java b/jdk/test/com/sun/jdi/ResumeOneThreadTest.java new file mode 100644 index 00000000000..aec4c0d7cca --- /dev/null +++ b/jdk/test/com/sun/jdi/ResumeOneThreadTest.java @@ -0,0 +1,233 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * @test + * @bug 6700889 + * @summary Thread resume invalidates all stack frames, even from other threads + * + * @author jjh + * + * @run build TestScaffold VMConnection TargetListener TargetAdapter + * @run compile -g ResumeOneThreadTest.java + * @run main ResumeOneThreadTest + */ +import com.sun.jdi.*; +import com.sun.jdi.event.*; +import com.sun.jdi.request.*; + +import java.util.*; + +class ResumeOneThreadTarg extends Thread { + static String name1 = "Thread 1"; + static String name2 = "Thread 2"; + + public ResumeOneThreadTarg(String name) { + super(name); + } + + public static void main(String[] args) { + System.out.println(" Debuggee: Howdy!"); + ResumeOneThreadTarg t1 = new ResumeOneThreadTarg(name1); + ResumeOneThreadTarg t2 = new ResumeOneThreadTarg(name2); + + t1.start(); + t2.start(); + } + + // This just starts two threads. Each runs to a bkpt. + public void run() { + if (getName().equals(name1)) { + run1(); + } else { + run2(); + } + } + + public void bkpt1(String p1) { + System.out.println(" Debuggee: bkpt 1"); + } + + public void run1() { + bkpt1("Hello Alviso!"); + } + + + + public void bkpt2() { + System.out.println(" Debuggee: bkpt 2"); + } + + public void run2() { + bkpt2(); + } +} + +/********** test program **********/ + +public class ResumeOneThreadTest extends TestScaffold { + ReferenceType targetClass; + ThreadReference mainThread; + + BreakpointRequest request1; + BreakpointRequest request2; + + ThreadReference thread1 = null; + ThreadReference thread2 = null;; + boolean theVMisDead = false; + + ResumeOneThreadTest (String args[]) { + super(args); + } + + public static void main(String[] args) throws Exception { + new ResumeOneThreadTest(args).startTests(); + } + + + synchronized public void breakpointReached(BreakpointEvent event) { + println("-- Got bkpt at: " + event.location()); + ThreadReference eventThread = event.thread(); + + if (eventThread.name().equals(ResumeOneThreadTarg.name1)) { + thread1 = eventThread; + } + + if (eventThread.name().equals(ResumeOneThreadTarg.name2)) { + thread2 = eventThread; + } + } + + public void vmDied(VMDeathEvent event) { + theVMisDead = true; + } + + synchronized public void eventSetComplete(EventSet set) { + if (theVMisDead) { + return; + } + if (thread1 == null || thread2 == null) { + // Don't do a set.resume(), just let the other thread + // keep running until it hits its bkpt. + return; + } + + // Both threads are stopped at their bkpts. Get a StackFrame from + // Thread 1 then resume Thread 2 and verify that the saved StackFrame is + // still valid. + + // suspend everything. + println("-- All threads suspended"); + vm().suspend(); + + StackFrame t1sf0 = null; + try { + t1sf0 = thread1.frame(0); + } catch (IncompatibleThreadStateException ee) { + failure("FAILED: Exception: " + ee); + } + + println("-- t1sf0 args: " + t1sf0.getArgumentValues()); + + // Ok, we have a StackFrame for thread 1. Resume just thread 2 + // Note that thread 2 has been suspended twice - by the SUSPEND_ALL + // bkpt, and by the above vm().suspend(), so we have to resume + // it twice. + request2.disable(); + + thread2.resume(); + thread2.resume(); + println("-- Did Resume on thread 2"); + + // Can we get frames for thread1? + try { + StackFrame t1sf0_1 = thread1.frame(0); + if (!t1sf0.equals(t1sf0_1)) { + failure("FAILED: Got a different frame 0 for thread 1 after resuming thread 2"); + } + } catch (IncompatibleThreadStateException ee) { + failure("FAILED: Could not get frames for thread 1: Exception: " + ee); + } catch (Exception ee) { + failure("FAILED: Could not get frames for thread 1: Exception: " + ee); + } + + + try { + println("-- t1sf0 args: " + t1sf0.getArgumentValues()); + } catch (InvalidStackFrameException ee) { + // This is the failure. + failure("FAILED Got InvalidStackFrameException"); + vm().dispose(); + throw(ee); + } + + // Let the debuggee finish + request1.disable(); + thread1.resume(); + vm().resume(); + println("--------------"); + } + + /********** test core **********/ + + protected void runTests() throws Exception { + + /* + * Get to the top of main() + * to determine targetClass and mainThread + */ + BreakpointEvent bpe = startToMain("ResumeOneThreadTarg"); + targetClass = bpe.location().declaringType(); + mainThread = bpe.thread(); + EventRequestManager erm = vm().eventRequestManager(); + final Thread mainThread = Thread.currentThread(); + + /* + * Set event requests + */ + + Location loc1 = findMethod(targetClass, "bkpt1", "(Ljava/lang/String;)V").location(); + request1 = erm.createBreakpointRequest(loc1); + request1.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + request1.enable(); + + Location loc2 = findMethod(targetClass, "bkpt2", "()V").location(); + request2 = erm.createBreakpointRequest(loc2); + request2.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + request2.enable(); + + /* + * resume the target, listening for events + */ + listenUntilVMDisconnect(); + /* + * deal with results of test + * if anything has called failure("foo") testFailed will be true + */ + if (!testFailed) { + println("ResumeOneThreadTest: passed"); + } else { + throw new Exception("ResumeOneThreadTest: failed"); + } + } +} From 634c79b98d2ed96cae1fe6892d160360f3b93f65 Mon Sep 17 00:00:00 2001 From: Kelly O'Hair Date: Wed, 9 Jul 2008 15:42:00 -0700 Subject: [PATCH 06/21] 6704966: OpenJDK README needs additional info on how to build freetype 6704968: OpenJDK Build README is missing ant requirement 6704973: OpenJDK Build readme needs cygwin package list improvements 6590549: Cygwin build of OpenJDK has problems and not very well documented 6462815: cygwin's gnumake 3.81-1 does not support MS-DOS path names 6597857: JDK build instructions need to be updated (BUILD_JDK_IMPORT_PATH, BUILD_BINARY_PLUGS_PATH) Reviewed-by: jjg, igor --- README-builds.html | 454 ++++++++++++++++++++++++++------------------- 1 file changed, 258 insertions(+), 196 deletions(-) diff --git a/README-builds.html b/README-builds.html index a2cf768e122..37998f9c1f6 100644 --- a/README-builds.html +++ b/README-builds.html @@ -5,15 +5,12 @@ - + +
    @@ -54,6 +51,7 @@
  • Bootstrap JDK
  • Binary Plugs
  • Optional Import JDK
  • +
  • Ant
  • Certificate Authority File (cacert)
  • Compilers
      @@ -424,24 +422,37 @@ you should use gmake which will be located in either the /opt/sfw/bin or /usr/sfw/bin directory. + In more recent versions of Solaris GNU make can be found + at /usr/bin/gmake.
    • Windows: Make sure you start your build inside a bash/sh/ksh shell.
      WARNING: Watch out for make version 3.81, it may - not work due to a lack of support for drive letter paths - like C:/. See - section on gmake. + not work due to a lack of support for MS-DOS drive letter paths + like C:/ or C:\. Use a 3.80 version, or find a newer - version that has this problem fixed. + version that has this problem fixed, like 3.82. The older 3.80 version of make.exe can be downloaded with this link. + Use of this older 3.80 make.exe may require that you install the + libintl2.dll library or libintl2 cygwin package which is + no longer installed by default by the cygwin installer. +
      Also see the mozilla developer center on this topic. +
      + It's hoped that when make 3.82 starts shipping in a future cygwin + release that this MS-DOS path issue will be fixed. + In addition to the above 3.80 make.exe you can download + this + + www.cmake.org make.exe which will not have a libintl2.dll + dependency.

    @@ -507,6 +518,11 @@ Install or upgrade the FreeType development package.

  • +
  • + Install + Ant, set + ANT_HOME. +
  • @@ -567,6 +583,11 @@ CUPS Include files, set ALT_CUPS_HEADERS_PATH. +
  • + Install + Ant, set + ANT_HOME. +
  • @@ -654,6 +675,11 @@ Install Microsoft DirectX SDK. +
  • + Install + Ant, set + ANT_HOME. +
  • @@ -736,6 +762,22 @@ and the build will copy the needed files from this import area. +

    Ant

    +
    + All OpenJDK builds require access to least Ant 1.6.5. + The Ant tool is available from the + + Ant download site. + You should always set + ANT_HOME + to point to the location of + the Ant installation, this is the directory pathname + that contains a bin and lib. + It's also a good idea to also place its bin directory + in the PATH environment variable, although it's + not absolutely required. +
    +

    Certificate Authority File (cacert)

    See @@ -915,6 +957,21 @@ and ALT_FREETYPE_HEADERS_PATH to refer to place where library and header files are installed. +

    + Building the freetype 2 libraries from scratch is also possible, + however on Windows refer to the + + Windows FreeType DLL build instructions. +

    + Note that by default FreeType is built with byte code hinting + support disabled due to licensing restrictions. + In this case, text appearance and metrics are expected to + differ from Sun's official JDK build. + See + + the SourceForge FreeType2 Home Page + + for more information.

    Advanced Linux Sound Architecture (ALSA) (Linux only)

    @@ -1036,7 +1093,8 @@ - + @@ -1050,7 +1108,7 @@ - + @@ -1061,17 +1119,17 @@ - + - + - + @@ -1224,46 +1282,6 @@ document) that can impact the build are:
    -
    PATH
    -
    Typically you want to set the PATH to include: -
      -
    • The location of the GNU make binary
    • -
    • The location of the Bootstrap JDK java - (see Bootstrap JDK)
    • -
    • The location of the C/C++ compilers - (see compilers)
    • -
    • The location or locations for the Unix command utilities - (e.g. /usr/bin)
    • -
    -
    -
    MILESTONE
    -
    - The milestone name for the build (e.g."beta"). - The default value is "internal". -
    -
    BUILD_NUMBER
    -
    - The build number for the build (e.g. "b27"). - The default value is "b00". -
    -
    ARCH_DATA_MODEL
    -
    The ARCH_DATA_MODEL variable - is used to specify whether the build is to generate 32-bit or 64-bit - binaries. - The Solaris build supports either 32-bit or 64-bit builds, but - Windows and Linux will support only one, depending on the specific - OS being used. - Normally, setting this variable is only necessary on Solaris. - Set ARCH_DATA_MODEL to 32 for generating 32-bit binaries, - or to 64 for generating 64-bit binaries. -
    -
    ALT_BOOTDIR
    -
    - The location of the bootstrap JDK installation. - See Bootstrap JDK for more information. - You should always install your own local Bootstrap JDK and - always set ALT_BOOTDIR explicitly. -
    ALT_BINARY_PLUGS_PATH
    The location of the binary plugs installation. @@ -1272,118 +1290,12 @@ recent Binary Plugs install image and set this variable to that location.
    -
    ALT_JDK_IMPORT_PATH
    +
    ALT_BOOTDIR
    - The location of a previously built JDK installation. - See Optional Import JDK for more information. -
    -
    ALT_OUTPUTDIR
    -
    - An override for specifying the (absolute) path of where the - build output is to go. - The default output directory will be build/platform. -
    -
    ALT_COMPILER_PATH
    -
    - The location of the C/C++ compiler. - The default varies depending on the platform. -
    -
    ALT_CACERTS_FILE
    -
    - The location of the cacerts file. - The default will refer to - jdk/src/share/lib/security/cacerts. -
    -
    ALT_CUPS_HEADERS_PATH
    -
    - The location of the CUPS header files. - See CUPS information for more information. - If this path does not exist the fallback path is - /usr/include. -
    -
    ALT_FREETYPE_LIB_PATH
    -
    - The location of the FreeType shared library. - See FreeType information for details. -
    -
    ALT_FREETYPE_HEADERS_PATH
    -
    - The location of the FreeType header files. - See FreeType information for details. -
    -
    ALT_JDK_DEVTOOLS_PATH
    -
    - The default root location of the devtools. - The default value is - $(ALT_SLASH_JAVA)/devtools. -
    -
    ALT_DEVTOOLS_PATH
    -
    - The location of tools like the - zip and unzip - binaries, but might also contain the GNU make utility - (gmake). - So this area is a bit of a grab bag, especially on Windows. - The default value depends on the platform and - Unix Commands being used. - On Linux the default will be - $(ALT_JDK_DEVTOOLS_PATH)/linux/bin, - on Solaris - $(ALT_JDK_DEVTOOLS_PATH)/{sparc,i386}/bin, - on Windows with MKS - %SYSTEMDRIVE%/UTILS, - and on Windows with CYGWIN - /usr/bin. -
    -
    ALT_UNIXCOMMAND_PATH
    -
    - An override for specifying where the - Unix command set are located. - The default location varies depending on the platform, - "%SYSTEMDRIVE%/MKSNT" or - $(ROOTDIR) on Windows with MKS, otherwise it's - "/bin" or /usr/bin. -
    -
    ALT_UNIXCCS_PATH
    -
    - Solaris only: - An override for specifying where the Unix CCS - command set are located. - The default location is /usr/ccs/bin -
    -
    ALT_USRBIN_PATH
    -
    - An override for specifying where the - Unix /usr/bin commands are located. You usually do not need - to set this variable: the default location is /usr/bin) -
    -
    ALT_SLASHJAVA
    -
    - The default root location for many of the ALT path locations - of the following ALT variables. - The default value is - "/java" on Solaris and Linux, - "J:" on Windows. -
    -
    ALT_BUILD_JDK_IMPORT_PATH
    -
    - These are useful in managing builds on multiple platforms. - The default network location for all of the import JDK images - for all platforms. - If ALT_JDK_IMPORT_PATH - is not set, this directory will be used and should contain - the following directories: - solaris-sparc, - solaris-i586, - solaris-sparcv9, - solaris-amd64, - linux-i586, - linux-amd64, - windows-i586, - and - windows-amd64. - Where each of these directories contain the import JDK image - for that platform. + The location of the bootstrap JDK installation. + See Bootstrap JDK for more information. + You should always install your own local Bootstrap JDK and + always set ALT_BOOTDIR explicitly.
    ALT_BUILD_BINARY_PLUGS_PATH
    @@ -1405,36 +1317,186 @@ Where each of these directories contain the binary plugs image for that platform.
    -
    Windows specific:
    +
    ALT_BUILD_JDK_IMPORT_PATH
    -
    -
    ALT_MSDEVTOOLS_PATH
    -
    - The location of the Microsoft Visual Studio .NET 2003 - tools 'bin' directory. - The default is usually derived from - ALT_COMPILER_PATH. -
    -
    ALT_DXSDK_PATH
    -
    - The location of the - Microsoft DirectX 9 SDK. - The default will be to try and use the DirectX environment - variable DXSDK_DIR, - failing that, look in C:/DXSDK. -
    -
    ALT_MSVCRT_DLL_PATH
    -
    - The location of the - MSVCRT.DLL. -
    -
    ALT_MSVCR71_DLL_PATH
    -
    - i586 only: - The location of the - MSVCR71.DLL. -
    -
    + These are useful in managing builds on multiple platforms. + The default network location for all of the import JDK images + for all platforms. + If ALT_JDK_IMPORT_PATH + is not set, this directory will be used and should contain + the following directories: + solaris-sparc, + solaris-i586, + solaris-sparcv9, + solaris-amd64, + linux-i586, + linux-amd64, + windows-i586, + and + windows-amd64. + Where each of these directories contain the import JDK image + for that platform. +
    +
    ALT_CACERTS_FILE
    +
    + The location of the cacerts file. + The default will refer to + jdk/src/share/lib/security/cacerts. +
    +
    ALT_COMPILER_PATH
    +
    + The location of the C/C++ compiler. + The default varies depending on the platform. +
    +
    ALT_CUPS_HEADERS_PATH
    +
    + The location of the CUPS header files. + See CUPS information for more information. + If this path does not exist the fallback path is + /usr/include. +
    +
    ALT_DEVTOOLS_PATH
    +
    + The location of tools like the + zip and unzip + binaries, but might also contain the GNU make utility + (gmake). + So this area is a bit of a grab bag, especially on Windows. + The default value depends on the platform and + Unix Commands being used. + On Linux the default will be + $(ALT_JDK_DEVTOOLS_PATH)/linux/bin, + on Solaris + $(ALT_JDK_DEVTOOLS_PATH)/{sparc,i386}/bin, + on Windows with MKS + %SYSTEMDRIVE%/UTILS, + and on Windows with CYGWIN + /usr/bin. +
    +
    ALT_DXSDK_PATH
    +
    + Windows Only: + The location of the + Microsoft DirectX 9 SDK. + The default will be to try and use the DirectX environment + variable DXSDK_DIR, + failing that, look in C:/DXSDK. +
    +
    ALT_FREETYPE_HEADERS_PATH
    +
    + The location of the FreeType header files. + See FreeType information for details. +
    +
    ALT_FREETYPE_LIB_PATH
    +
    + The location of the FreeType shared library. + See FreeType information for details. +
    +
    ALT_JDK_DEVTOOLS_PATH
    +
    + The default root location of the devtools. + The default value is + $(ALT_SLASH_JAVA)/devtools. +
    +
    ALT_JDK_IMPORT_PATH
    +
    + The location of a previously built JDK installation. + See Optional Import JDK for more information. +
    +
    ALT_MSDEVTOOLS_PATH
    +
    + Windows Only: + The location of the Microsoft Visual Studio .NET 2003 + tools 'bin' directory. + The default is usually derived from + ALT_COMPILER_PATH. +
    +
    ALT_MSVCR71_DLL_PATH
    +
    + Windows i586 only: + The location of the + MSVCR71.DLL. +
    +
    ALT_MSVCRT_DLL_PATH
    +
    + Windows Only: + The location of the + MSVCRT.DLL. +
    +
    ALT_OUTPUTDIR
    +
    + An override for specifying the (absolute) path of where the + build output is to go. + The default output directory will be build/platform. +
    +
    ALT_SLASHJAVA
    +
    + The default root location for many of the ALT path locations + of the following ALT variables. + The default value is + "/java" on Solaris and Linux, + "J:" on Windows. +
    +
    ALT_UNIXCCS_PATH
    +
    + Solaris only: + An override for specifying where the Unix CCS + command set are located. + The default location is /usr/ccs/bin +
    +
    ALT_UNIXCOMMAND_PATH
    +
    + An override for specifying where the + Unix command set are located. + The default location varies depending on the platform, + "%SYSTEMDRIVE%/MKSNT" or + $(ROOTDIR) on Windows with MKS, otherwise it's + "/bin" or /usr/bin. +
    +
    ALT_USRBIN_PATH
    +
    + An override for specifying where the + Unix /usr/bin commands are located. You usually do not need + to set this variable: the default location is /usr/bin) +
    +
    ANT_HOME
    +
    + The location of the Ant installation. + See Ant for more information. + You should always set ANT_HOME explicitly. +
    +
    ARCH_DATA_MODEL
    +
    The ARCH_DATA_MODEL variable + is used to specify whether the build is to generate 32-bit or 64-bit + binaries. + The Solaris build supports either 32-bit or 64-bit builds, but + Windows and Linux will support only one, depending on the specific + OS being used. + Normally, setting this variable is only necessary on Solaris. + Set ARCH_DATA_MODEL to 32 for generating 32-bit binaries, + or to 64 for generating 64-bit binaries. +
    +
    BUILD_NUMBER
    +
    + The build number for the build (e.g. "b27"). + The default value is "b00". +
    +
    MILESTONE
    +
    + The milestone name for the build (e.g."beta"). + The default value is "internal". +
    +
    PATH
    +
    Typically you want to set the PATH to include: +
      +
    • The location of the GNU make binary
    • +
    • The location of the Bootstrap JDK java + (see Bootstrap JDK)
    • +
    • The location of the C/C++ compilers + (see compilers)
    • +
    • The location or locations for the Unix command utilities + (e.g. /usr/bin)
    • +
    From 040fbc1535a0b7e209eae94c321daf3aab2d5ca9 Mon Sep 17 00:00:00 2001 From: Jim Holmlund Date: Fri, 11 Jul 2008 17:06:54 -0700 Subject: [PATCH 07/21] 6725192: Some JDI Regression tests fail if @debuggeeVMOptions contain multiple blanks Fix to allow mutiple blanks between options Reviewed-by: tbell --- jdk/test/com/sun/jdi/VMConnection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/test/com/sun/jdi/VMConnection.java b/jdk/test/com/sun/jdi/VMConnection.java index f370bf46b8a..c9cb83f7257 100644 --- a/jdk/test/com/sun/jdi/VMConnection.java +++ b/jdk/test/com/sun/jdi/VMConnection.java @@ -115,7 +115,7 @@ class VMConnection { return cmdLine; } // Insert the options at position 1. Blanks in args are not allowed! - String[] v1 = opts.split(" "); + String[] v1 = opts.split(" +"); String[] retVal = new String[cmdLine.length + v1.length]; retVal[0] = cmdLine[0]; System.arraycopy(v1, 0, retVal, 1, v1.length); From 2270e88840c88423032d392e0ad2a6731f9e8d44 Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Mon, 14 Jul 2008 14:03:42 -0700 Subject: [PATCH 08/21] 6608833: (se spec) SelectionKey @throws references wrong param Update the API doc to correct the wrong description Reviewed-by: alanb --- jdk/src/share/classes/java/nio/channels/SelectionKey.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/src/share/classes/java/nio/channels/SelectionKey.java b/jdk/src/share/classes/java/nio/channels/SelectionKey.java index 7acb692ccd0..00fd36f463a 100644 --- a/jdk/src/share/classes/java/nio/channels/SelectionKey.java +++ b/jdk/src/share/classes/java/nio/channels/SelectionKey.java @@ -191,7 +191,7 @@ public abstract class SelectionKey { * @throws IllegalArgumentException * If a bit in the set does not correspond to an operation that * is supported by this key's channel, that is, if - * set & ~(channel().validOps()) != 0 + * (ops & ~channel().validOps()) != 0 * * @throws CancelledKeyException * If this key has been cancelled From c65bf79ac2ec24c4906f9958d6750aa74ec0e949 Mon Sep 17 00:00:00 2001 From: Kelly O'Hair Date: Wed, 16 Jul 2008 09:51:43 -0700 Subject: [PATCH 09/21] 6548261: Use of SE in make/common/Defs-windows.gmk Reviewed-by: darcy --- jdk/make/common/Defs-windows.gmk | 29 ++++++++++++++++++----------- jdk/make/common/Defs.gmk | 2 +- jdk/make/common/shared/Defs.gmk | 14 ++++++++------ 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/jdk/make/common/Defs-windows.gmk b/jdk/make/common/Defs-windows.gmk index 5bb50249c9d..51982dc1a1b 100644 --- a/jdk/make/common/Defs-windows.gmk +++ b/jdk/make/common/Defs-windows.gmk @@ -404,7 +404,6 @@ ifdef JDK_UPDATE_VERSION else JDK_UPDATE_VER := 0 endif -JDK_VER = $(JDK_MINOR_VERSION),$(JDK_MICRO_VERSION),$(JDK_UPDATE_VER),$(COOKED_BUILD_NUMBER) RC_FLAGS = /l 0x409 /r @@ -414,15 +413,23 @@ else RC_FLAGS += $(MS_RC_DEBUG_OPTION) endif -ifndef COPYRIGHT_YEAR - COPYRIGHT_YEAR = 2007 -endif +# Values for the RC variables defined in RC_FLAGS +JDK_RC_BUILD_ID = $(FULL_VERSION) +JDK_RC_COMPANY = $(COMPANY_NAME) +JDK_RC_COMPONENT = $(PRODUCT_NAME) $(JDK_RC_PLATFORM_NAME) binary +JDK_RC_VER = \ + $(JDK_MINOR_VERSION).$(JDK_MICRO_VERSION).$(JDK_UPDATE_VER).$(COOKED_BUILD_NUMBER) +JDK_RC_COPYRIGHT = Copyright \xA9 $(COPYRIGHT_YEAR) +JDK_RC_NAME = \ + $(PRODUCT_NAME) $(JDK_RC_PLATFORM_NAME) $(JDK_MINOR_VERSION) $(JDK_UPDATE_META_TAG) +JDK_RC_FVER = \ + $(JDK_MINOR_VERSION),$(JDK_MICRO_VERSION),$(JDK_UPDATE_VER),$(COOKED_BUILD_NUMBER) # J2SE name required here -RC_FLAGS += -d "J2SE_BUILD_ID=$(FULL_VERSION)" \ - -d "J2SE_COMPANY=$(COMPANY_NAME)" \ - -d "J2SE_COMPONENT=$(PRODUCT_NAME) Platform SE binary" \ - -d "J2SE_VER=$(JDK_MINOR_VERSION).$(JDK_MICRO_VERSION).$(JDK_UPDATE_VER).$(COOKED_BUILD_NUMBER)" \ - -d "J2SE_COPYRIGHT=Copyright \xA9 $(COPYRIGHT_YEAR)" \ - -d "J2SE_NAME=$(PRODUCT_NAME) Platform SE $(JDK_MINOR_VERSION) $(JDK_UPDATE_META_TAG)" \ - -d "J2SE_FVER=$(JDK_VER)" +RC_FLAGS += -d "J2SE_BUILD_ID=$(JDK_RC_BUILD_ID)" \ + -d "J2SE_COMPANY=$(JDK_RC_COMPANY)" \ + -d "J2SE_COMPONENT=$(JDK_RC_COMPONENT)" \ + -d "J2SE_VER=$(JDK_RC_VER)" \ + -d "J2SE_COPYRIGHT=$(JDK_RC_COPYRIGHT)" \ + -d "J2SE_NAME=$(JDK_RC_NAME)" \ + -d "J2SE_FVER=$(JDK_RC_FVER)" diff --git a/jdk/make/common/Defs.gmk b/jdk/make/common/Defs.gmk index 1c72ff99dfe..16eb1e2258f 100644 --- a/jdk/make/common/Defs.gmk +++ b/jdk/make/common/Defs.gmk @@ -703,7 +703,7 @@ endif ifdef ALT_COPYRIGHT_YEAR COPYRIGHT_YEAR = $(ALT_COPYRIGHT_YEAR) else - COPYRIGHT_YEAR = $(shell $(DATE) '+%Y') + COPYRIGHT_YEAR := $(shell $(DATE) '+%Y') endif # Install of imported file (JDK_IMPORT_PATH, or some other external location) diff --git a/jdk/make/common/shared/Defs.gmk b/jdk/make/common/shared/Defs.gmk index 8fc28e239e0..af86be8085d 100644 --- a/jdk/make/common/shared/Defs.gmk +++ b/jdk/make/common/shared/Defs.gmk @@ -188,16 +188,18 @@ ifndef MILESTONE endif # Default names -LAUNCHER_NAME = java -PRODUCT_NAME = Java(TM) -PRODUCT_SUFFIX = SE Runtime Environment -COMPANY_NAME = Sun Microsystems, Inc. - ifdef OPENJDK LAUNCHER_NAME = openjdk PRODUCT_NAME = OpenJDK PRODUCT_SUFFIX = Runtime Environment - COMPANY_NAME = + JDK_RC_PLATFORM_NAME = Platform + COMPANY_NAME = N/A +else + LAUNCHER_NAME = java + PRODUCT_NAME = Java(TM) + PRODUCT_SUFFIX = SE Runtime Environment + JDK_RC_PLATFORM_NAME = Platform SE + COMPANY_NAME = Sun Microsystems, Inc. endif RUNTIME_NAME = $(PRODUCT_NAME) $(PRODUCT_SUFFIX) From 61dd937d11c65ecd54ee3544fb62b6398efe723e Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Wed, 16 Jul 2008 15:09:24 -0700 Subject: [PATCH 10/21] 6541641: (fc) java/nio/channels/FileChannel/ExpandingMap.java should clean up after itself Close channel and explicitly de-ref the mapped buffers before exit. Reviewed-by: alanb --- .../java/nio/channels/FileChannel/ExpandingMap.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/jdk/test/java/nio/channels/FileChannel/ExpandingMap.java b/jdk/test/java/nio/channels/FileChannel/ExpandingMap.java index 23ef97a6bbf..278badb9702 100644 --- a/jdk/test/java/nio/channels/FileChannel/ExpandingMap.java +++ b/jdk/test/java/nio/channels/FileChannel/ExpandingMap.java @@ -22,7 +22,7 @@ */ /* @test - * @bug 4938372 + * @bug 4938372 6541641 * @summary Flushing dirty pages prior to unmap can cause Cleaner thread to * abort VM if memory system has pages locked */ @@ -39,7 +39,7 @@ import java.util.ArrayList; public class ExpandingMap { - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws Exception { int initialSize = 20480*1024; int maximumMapSize = 16*1024*1024; @@ -103,6 +103,13 @@ public class ExpandingMap { } } + fc.close(); + // cleanup the ref to mapped buffers so they can be GCed + for (int i = 0; i < buffers.length; i++) + buffers[i] = null; + System.gc(); + // Take a nap to wait for the Cleaner to cleanup those unrefed maps + Thread.sleep(1000); System.out.println("TEST PASSED"); } From c09d716d345ac0c9d31f63845220669ec32102b0 Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Wed, 16 Jul 2008 15:24:15 -0700 Subject: [PATCH 11/21] 6429289: (se) sun.nio.ch.SelectorImpl.processDeregisterQueue creates excessive garbage Check if the cancelledKeys is empty or not before creating iterator Reviewed-by: alanb --- .../classes/sun/nio/ch/SelectorImpl.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/jdk/src/share/classes/sun/nio/ch/SelectorImpl.java b/jdk/src/share/classes/sun/nio/ch/SelectorImpl.java index 828a37c712d..f092db00d79 100644 --- a/jdk/src/share/classes/sun/nio/ch/SelectorImpl.java +++ b/jdk/src/share/classes/sun/nio/ch/SelectorImpl.java @@ -142,18 +142,20 @@ abstract class SelectorImpl // Precondition: Synchronized on this, keys, and selectedKeys Set cks = cancelledKeys(); synchronized (cks) { - Iterator i = cks.iterator(); - while (i.hasNext()) { - SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); - try { - implDereg(ski); - } catch (SocketException se) { - IOException ioe = new IOException( - "Error deregistering key"); - ioe.initCause(se); - throw ioe; - } finally { - i.remove(); + if (!cks.isEmpty()) { + Iterator i = cks.iterator(); + while (i.hasNext()) { + SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); + try { + implDereg(ski); + } catch (SocketException se) { + IOException ioe = new IOException( + "Error deregistering key"); + ioe.initCause(se); + throw ioe; + } finally { + i.remove(); + } } } } From 873c5789943abbb6b5825672b0dc6179fcf8cd9c Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Thu, 17 Jul 2008 11:28:31 -0700 Subject: [PATCH 12/21] Added tag jdk7-b31 for changeset d9dc137c39e1 --- .hgtags-top-repo | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags-top-repo b/.hgtags-top-repo index cec8ccbe986..ce58a609d1b 100644 --- a/.hgtags-top-repo +++ b/.hgtags-top-repo @@ -5,3 +5,4 @@ cbc8ad9dd0e085a607427ea35411990982f19a36 jdk7-b25 56652b46f328937f6b9b5130f1e4cd80f48868ef jdk7-b28 31e08f70e88d77c2053f91c21b49a04296bdc59a jdk7-b29 2dab2f712e1832c92acfa63ec0337048b9422c20 jdk7-b30 +3300a35a0bd56d695b92fe0b34f03ebbfc939064 jdk7-b31 From 271059534c0da72db79409800216145450f5e3d3 Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Thu, 17 Jul 2008 11:28:32 -0700 Subject: [PATCH 13/21] Added tag jdk7-b31 for changeset 918ff19c1a3a --- corba/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/corba/.hgtags b/corba/.hgtags index c2bfd6ff394..6792e20a86a 100644 --- a/corba/.hgtags +++ b/corba/.hgtags @@ -5,3 +5,4 @@ e84e9018bebbf3e5bafc5706e7882a15cb1c7d99 jdk7-b27 27509b7d21ed783b3f6eb7b7612781c675a30c2f jdk7-b28 8b71960f79ce0a6fb8ddfeec03f03d400a361747 jdk7-b29 c0252adbb2abbfdd6c35595429ac6fbdd98e20ac jdk7-b30 +ef6af34d75a7b44e77083f1d4ee47631fa09d3b4 jdk7-b31 From c04b76a222b995a7701e9e435dbbdc36d1fd845f Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Thu, 17 Jul 2008 11:28:34 -0700 Subject: [PATCH 14/21] Added tag jdk7-b31 for changeset e1baa9c8f16f --- hotspot/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/hotspot/.hgtags b/hotspot/.hgtags index 1c7dc2a0349..b23c6ac9012 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -5,3 +5,4 @@ e3d2692f8442e2d951166dc9bd9a330684754438 jdk7-b27 c14dab40ed9bf45ad21150bd70c9c80cdf655415 jdk7-b28 4f91c08b3e4498213a9c5a24898f7d9c38cf86fb jdk7-b29 d1605aabd0a15ecf93787c47de63073c33fba52d jdk7-b30 +9c2ecc2ffb125f14fab3857fe7689598956348a0 jdk7-b31 From 702349555710906e18d9fcb09dc2d132d0975042 Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Thu, 17 Jul 2008 11:28:38 -0700 Subject: [PATCH 15/21] Added tag jdk7-b31 for changeset 2378316adee0 --- jaxp/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jaxp/.hgtags b/jaxp/.hgtags index f4f91fe3c71..c749c17a66d 100644 --- a/jaxp/.hgtags +++ b/jaxp/.hgtags @@ -5,3 +5,4 @@ bafed478d67c3acf7744aaad88b9404261ea6739 jdk7-b27 b996318955c0ad8e9fa0ffb56c74f626786e863f jdk7-b28 617ee8607cfd5fd81f233f3c0b690f85084687a0 jdk7-b29 2d94a238a1641d074e6032dcdceed461d6f85d6a jdk7-b30 +255d64ee287e926e8629dd80bc67690e65eeba30 jdk7-b31 From 4a6a92120161a5f6bed7baf54b2324158b660c0e Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Thu, 17 Jul 2008 11:28:40 -0700 Subject: [PATCH 16/21] Added tag jdk7-b31 for changeset 3e8ca32a629a --- jaxws/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jaxws/.hgtags b/jaxws/.hgtags index 8bfe2a4df5c..0224fa8b6b1 100644 --- a/jaxws/.hgtags +++ b/jaxws/.hgtags @@ -5,3 +5,4 @@ debd37e1a422e580edb086c95d6e89199133a39c jdk7-b26 eefcd5204500a11d6aa802dca9f961cf10ab64c2 jdk7-b28 836c55713abab186e4de0c6eabd06ff008c7b8d0 jdk7-b29 7f2466f8cc7009702e548d1a763254f546024d7e jdk7-b30 +f978623825364a2ad9c6f51d02fc9424a8b0bc86 jdk7-b31 From 8b4c5a01d24a84a8f4d4fa7b99092d253de35fb2 Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Thu, 17 Jul 2008 11:28:44 -0700 Subject: [PATCH 17/21] Added tag jdk7-b31 for changeset 9a19b12225f4 --- jdk/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/.hgtags b/jdk/.hgtags index 282129a897c..9da57349ed1 100644 --- a/jdk/.hgtags +++ b/jdk/.hgtags @@ -5,3 +5,4 @@ fb57027902e04ecafceae31a605e69b436c23d57 jdk7-b26 02e4c5348592a8d7fc2cba28bc5f8e35c0e17277 jdk7-b28 e21f4266466cd1306b176aaa08b2cd8337a9be3d jdk7-b29 b6d6877c1155621a175dccd12dc14c54f938fb8b jdk7-b30 +b7474b739d13bacd9972f88ac91f6350b7b0be12 jdk7-b31 From 5628d956d5473f3786641ebe1a3e389cc0ae7916 Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Thu, 17 Jul 2008 14:26:51 -0700 Subject: [PATCH 18/21] 6405995: (ch) test/java/nio/channels/Selector/Wakeup.java broken Moved the unit test case back to open repo, it works after bugfixes in jdk6 Reviewed-by: alanb --- .../java/nio/channels/Selector/Wakeup.java | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 jdk/test/java/nio/channels/Selector/Wakeup.java diff --git a/jdk/test/java/nio/channels/Selector/Wakeup.java b/jdk/test/java/nio/channels/Selector/Wakeup.java new file mode 100644 index 00000000000..808ba831653 --- /dev/null +++ b/jdk/test/java/nio/channels/Selector/Wakeup.java @@ -0,0 +1,197 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6405995 + * @summary Unit test for selector wakeup and interruption + * @library .. + */ + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.Random; + +public class Wakeup { + + static void sleep(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException x) { + x.printStackTrace(); + } + } + + static class Sleeper extends TestThread { + volatile boolean started = false; + volatile int entries = 0; + volatile int wakeups = 0; + volatile boolean wantInterrupt = false; + volatile boolean gotInterrupt = false; + volatile Exception exception = null; + volatile boolean closed = false; + Object gate = new Object(); + + Selector sel; + + Sleeper(Selector sel) { + super("Sleeper", System.err); + this.sel = sel; + } + + public void go() throws Exception { + started = true; + for (;;) { + synchronized (gate) { } + entries++; + try { + sel.select(); + } catch (ClosedSelectorException x) { + closed = true; + } + boolean intr = Thread.currentThread().isInterrupted(); + wakeups++; + System.err.println("Wakeup " + wakeups + + (closed ? " (closed)" : "") + + (intr ? " (intr)" : "")); + if (wakeups > 1000) + throw new Exception("Too many wakeups"); + if (closed) + return; + if (wantInterrupt) { + while (!Thread.interrupted()) + Thread.yield(); + gotInterrupt = true; + wantInterrupt = false; + } + } + } + + } + + private static int checkedWakeups = 0; + + private static void check(Sleeper sleeper, boolean intr) + throws Exception + { + checkedWakeups++; + if (sleeper.wakeups > checkedWakeups) { + sleeper.finish(100); + throw new Exception("Sleeper has run ahead"); + } + int n = 0; + while (sleeper.wakeups < checkedWakeups) { + sleep(50); + if ((n += 50) > 1000) { + sleeper.finish(100); + throw new Exception("Sleeper appears to be dead (" + + checkedWakeups + ")"); + } + } + if (sleeper.wakeups > checkedWakeups) { + sleeper.finish(100); + throw new Exception("Too many wakeups: Expected " + + checkedWakeups + + ", got " + sleeper.wakeups); + } + if (intr) { + n = 0; + // Interrupts can sometimes be delayed, so wait + while (!sleeper.gotInterrupt) { + sleep(50); + if ((n += 50) > 1000) { + sleeper.finish(100); + throw new Exception("Interrupt never delivered"); + } + } + sleeper.gotInterrupt = false; + } + System.err.println("Check " + checkedWakeups + + (intr ? " (intr " + n + ")" : "")); + } + + public static void main(String[] args) throws Exception { + + Selector sel = Selector.open(); + + // Wakeup before select + sel.wakeup(); + + Sleeper sleeper = new Sleeper(sel); + + sleeper.start(); + while (!sleeper.started) + sleep(50); + + check(sleeper, false); // 1 + + for (int i = 2; i < 5; i++) { + // Wakeup during select + sel.wakeup(); + check(sleeper, false); // 2 .. 4 + } + + // Double wakeup + synchronized (sleeper.gate) { + sel.wakeup(); + check(sleeper, false); // 5 + sel.wakeup(); + sel.wakeup(); + } + check(sleeper, false); // 6 + + // Interrupt + synchronized (sleeper.gate) { + sleeper.wantInterrupt = true; + sleeper.interrupt(); + check(sleeper, true); // 7 + } + + // Interrupt before select + while (sleeper.entries < 8) + Thread.yield(); + synchronized (sleeper.gate) { + sel.wakeup(); + check(sleeper, false); // 8 + sleeper.wantInterrupt = true; + sleeper.interrupt(); + sleep(50); + } + check(sleeper, true); // 9 + + // Close during select + while (sleeper.entries < 10) + Thread.yield(); + synchronized (sleeper.gate) { + sel.close(); + check(sleeper, false); // 10 + } + + if (sleeper.finish(200) == 0) + throw new Exception("Test failed"); + if (!sleeper.closed) + throw new Exception("Selector not closed"); + } + +} From 20dba03e99189bf3c1d3fcdeacfe7e41a49eaeaf Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Sat, 19 Jul 2008 17:38:22 -0400 Subject: [PATCH 19/21] 6716785: implicit null checks not triggering with CompressedOops Allocate alignment-sized page(s) below java heap so that memory accesses at heap_base+1page give signal and cause an implicit null check Reviewed-by: kvn, jmasa, phh, jcoomes --- hotspot/src/os/linux/vm/os_linux.cpp | 19 +++- hotspot/src/os/solaris/vm/os_solaris.cpp | 17 +++- hotspot/src/os/windows/vm/os_windows.cpp | 29 +++++- .../linux_sparc/vm/assembler_linux_sparc.cpp | 6 -- .../linux_x86/vm/assembler_linux_x86_32.cpp | 7 -- .../linux_x86/vm/assembler_linux_x86_64.cpp | 19 ---- .../vm/assembler_solaris_sparc.cpp | 12 --- .../vm/assembler_solaris_x86_32.cpp | 6 -- .../vm/assembler_solaris_x86_64.cpp | 19 ---- .../vm/assembler_windows_x86_32.cpp | 4 - .../vm/assembler_windows_x86_64.cpp | 16 ---- hotspot/src/share/vm/asm/assembler.cpp | 18 ++++ .../parallelScavenge/parMarkBitMap.cpp | 2 + .../parallelScavenge/parallelScavengeHeap.cpp | 4 +- .../parallelScavenge/psParallelCompact.cpp | 2 + .../parallelScavenge/psVirtualspace.cpp | 9 +- .../src/share/vm/memory/genCollectedHeap.cpp | 4 +- hotspot/src/share/vm/prims/jni.cpp | 3 +- hotspot/src/share/vm/runtime/arguments.cpp | 4 +- hotspot/src/share/vm/runtime/os.cpp | 5 +- hotspot/src/share/vm/runtime/os.hpp | 6 +- hotspot/src/share/vm/runtime/virtualspace.cpp | 93 ++++++++++++++++--- hotspot/src/share/vm/runtime/virtualspace.hpp | 26 +++++- 23 files changed, 197 insertions(+), 133 deletions(-) diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp index 4c7ab6f6dd1..bc5d9ebc69e 100644 --- a/hotspot/src/os/linux/vm/os_linux.cpp +++ b/hotspot/src/os/linux/vm/os_linux.cpp @@ -2414,8 +2414,20 @@ static bool linux_mprotect(char* addr, size_t size, int prot) { return ::mprotect(bottom, size, prot) == 0; } -bool os::protect_memory(char* addr, size_t size) { - return linux_mprotect(addr, size, PROT_READ); +// Set protections specified +bool os::protect_memory(char* addr, size_t bytes, ProtType prot, + bool is_committed) { + unsigned int p = 0; + switch (prot) { + case MEM_PROT_NONE: p = PROT_NONE; break; + case MEM_PROT_READ: p = PROT_READ; break; + case MEM_PROT_RW: p = PROT_READ|PROT_WRITE; break; + case MEM_PROT_RWX: p = PROT_READ|PROT_WRITE|PROT_EXEC; break; + default: + ShouldNotReachHere(); + } + // is_committed is unused. + return linux_mprotect(addr, bytes, p); } bool os::guard_memory(char* addr, size_t size) { @@ -3704,8 +3716,9 @@ void os::make_polling_page_unreadable(void) { // Mark the polling page as readable void os::make_polling_page_readable(void) { - if( !protect_memory((char *)_polling_page, Linux::page_size()) ) + if( !linux_mprotect((char *)_polling_page, Linux::page_size(), PROT_READ)) { fatal("Could not enable polling page"); + } }; int os::active_processor_count() { diff --git a/hotspot/src/os/solaris/vm/os_solaris.cpp b/hotspot/src/os/solaris/vm/os_solaris.cpp index 952706c66b3..b6ca2a6db90 100644 --- a/hotspot/src/os/solaris/vm/os_solaris.cpp +++ b/hotspot/src/os/solaris/vm/os_solaris.cpp @@ -2965,10 +2965,21 @@ static bool solaris_mprotect(char* addr, size_t bytes, int prot) { return retVal == 0; } -// Protect memory (make it read-only. (Used to pass readonly pages through +// Protect memory (Used to pass readonly pages through // JNI GetArrayElements with empty arrays.) -bool os::protect_memory(char* addr, size_t bytes) { - return solaris_mprotect(addr, bytes, PROT_READ); +bool os::protect_memory(char* addr, size_t bytes, ProtType prot, + bool is_committed) { + unsigned int p = 0; + switch (prot) { + case MEM_PROT_NONE: p = PROT_NONE; break; + case MEM_PROT_READ: p = PROT_READ; break; + case MEM_PROT_RW: p = PROT_READ|PROT_WRITE; break; + case MEM_PROT_RWX: p = PROT_READ|PROT_WRITE|PROT_EXEC; break; + default: + ShouldNotReachHere(); + } + // is_committed is unused. + return solaris_mprotect(addr, bytes, p); } // guard_memory and unguard_memory only happens within stack guard pages. diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp index 97b2d752a28..14b3141d27e 100644 --- a/hotspot/src/os/windows/vm/os_windows.cpp +++ b/hotspot/src/os/windows/vm/os_windows.cpp @@ -2170,6 +2170,7 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { // Windows 98 reports faulting addresses incorrectly if (!MacroAssembler::needs_explicit_null_check((intptr_t)addr) || !os::win32::is_nt()) { + return Handle_Exception(exceptionInfo, SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL)); } @@ -2563,9 +2564,33 @@ bool os::release_memory(char* addr, size_t bytes) { return VirtualFree(addr, 0, MEM_RELEASE) != 0; } -bool os::protect_memory(char* addr, size_t bytes) { +// Set protections specified +bool os::protect_memory(char* addr, size_t bytes, ProtType prot, + bool is_committed) { + unsigned int p = 0; + switch (prot) { + case MEM_PROT_NONE: p = PAGE_NOACCESS; break; + case MEM_PROT_READ: p = PAGE_READONLY; break; + case MEM_PROT_RW: p = PAGE_READWRITE; break; + case MEM_PROT_RWX: p = PAGE_EXECUTE_READWRITE; break; + default: + ShouldNotReachHere(); + } + DWORD old_status; - return VirtualProtect(addr, bytes, PAGE_READONLY, &old_status) != 0; + + // Strange enough, but on Win32 one can change protection only for committed + // memory, not a big deal anyway, as bytes less or equal than 64K + if (!is_committed && !commit_memory(addr, bytes)) { + fatal("cannot commit protection page"); + } + // One cannot use os::guard_memory() here, as on Win32 guard page + // have different (one-shot) semantics, from MSDN on PAGE_GUARD: + // + // Pages in the region become guard pages. Any attempt to access a guard page + // causes the system to raise a STATUS_GUARD_PAGE exception and turn off + // the guard page status. Guard pages thus act as a one-time access alarm. + return VirtualProtect(addr, bytes, p, &old_status) != 0; } bool os::guard_memory(char* addr, size_t bytes) { diff --git a/hotspot/src/os_cpu/linux_sparc/vm/assembler_linux_sparc.cpp b/hotspot/src/os_cpu/linux_sparc/vm/assembler_linux_sparc.cpp index 95488f108cc..0fcd3b0d6d8 100644 --- a/hotspot/src/os_cpu/linux_sparc/vm/assembler_linux_sparc.cpp +++ b/hotspot/src/os_cpu/linux_sparc/vm/assembler_linux_sparc.cpp @@ -27,12 +27,6 @@ #include -bool MacroAssembler::needs_explicit_null_check(intptr_t offset) { - // Since the linux kernel resides at the low end of - // user address space, no null pointer check is needed. - return offset < 0 || offset >= 0x100000; -} - void MacroAssembler::read_ccr_trap(Register ccr_save) { // No implementation breakpoint_trap(); diff --git a/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_32.cpp b/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_32.cpp index 6de7ee2811e..1854b007516 100644 --- a/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_32.cpp +++ b/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_32.cpp @@ -39,10 +39,3 @@ void MacroAssembler::get_thread(Register thread) { movptr(thread, tls); } - -bool MacroAssembler::needs_explicit_null_check(intptr_t offset) { - // Linux kernel guarantees that the first page is always unmapped. Don't - // assume anything more than that. - bool offset_in_first_page = 0 <= offset && offset < os::vm_page_size(); - return !offset_in_first_page; -} diff --git a/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_64.cpp b/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_64.cpp index 2a817f3adb0..24a4dce09e4 100644 --- a/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_64.cpp +++ b/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_64.cpp @@ -65,22 +65,3 @@ void MacroAssembler::get_thread(Register thread) { popq(rax); } } - -bool MacroAssembler::needs_explicit_null_check(intptr_t offset) { - // Exception handler checks the nmethod's implicit null checks table - // only when this method returns false. - if (UseCompressedOops) { - // The first page after heap_base is unmapped and - // the 'offset' is equal to [heap_base + offset] for - // narrow oop implicit null checks. - uintptr_t heap_base = (uintptr_t)Universe::heap_base(); - if ((uintptr_t)offset >= heap_base) { - // Normalize offset for the next check. - offset = (intptr_t)(pointer_delta((void*)offset, (void*)heap_base, 1)); - } - } - // Linux kernel guarantees that the first page is always unmapped. Don't - // assume anything more than that. - bool offset_in_first_page = 0 <= offset && offset < os::vm_page_size(); - return !offset_in_first_page; -} diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/assembler_solaris_sparc.cpp b/hotspot/src/os_cpu/solaris_sparc/vm/assembler_solaris_sparc.cpp index 8e18dde9c31..caab18f5dcd 100644 --- a/hotspot/src/os_cpu/solaris_sparc/vm/assembler_solaris_sparc.cpp +++ b/hotspot/src/os_cpu/solaris_sparc/vm/assembler_solaris_sparc.cpp @@ -28,18 +28,6 @@ #include // For trap numbers #include // For V8 compatibility -bool MacroAssembler::needs_explicit_null_check(intptr_t offset) { - // The first page of virtual addresses is unmapped on SPARC. - // Thus, any access the VM makes through a null pointer with an offset of - // less than 4K will get a recognizable SIGSEGV, which the signal handler - // will transform into a NullPointerException. - // (Actually, the first 64K or so is unmapped, but it's simpler - // to depend only on the first 4K or so.) - - bool offset_in_first_page = 0 <= offset && offset < os::vm_page_size(); - return !offset_in_first_page; -} - void MacroAssembler::read_ccr_trap(Register ccr_save) { // Execute a trap to get the PSR, mask and shift // to get the condition codes. diff --git a/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_32.cpp b/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_32.cpp index 30a66f317c0..bce611c1125 100644 --- a/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_32.cpp +++ b/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_32.cpp @@ -79,9 +79,3 @@ void MacroAssembler::get_thread(Register thread) { if (thread != rax) popl(rax); popl(thread); } - -bool MacroAssembler::needs_explicit_null_check(intptr_t offset) { - // Identical to Sparc/Solaris code - bool offset_in_first_page = 0 <= offset && offset < os::vm_page_size(); - return !offset_in_first_page; -} diff --git a/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_64.cpp b/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_64.cpp index 1e84101e200..2ccae8a683d 100644 --- a/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_64.cpp +++ b/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_64.cpp @@ -85,22 +85,3 @@ void MacroAssembler::get_thread(Register thread) { popq(rax); } } - -bool MacroAssembler::needs_explicit_null_check(intptr_t offset) { - // Identical to Sparc/Solaris code - - // Exception handler checks the nmethod's implicit null checks table - // only when this method returns false. - if (UseCompressedOops) { - // The first page after heap_base is unmapped and - // the 'offset' is equal to [heap_base + offset] for - // narrow oop implicit null checks. - uintptr_t heap_base = (uintptr_t)Universe::heap_base(); - if ((uintptr_t)offset >= heap_base) { - // Normalize offset for the next check. - offset = (intptr_t)(pointer_delta((void*)offset, (void*)heap_base, 1)); - } - } - bool offset_in_first_page = 0 <= offset && offset < os::vm_page_size(); - return !offset_in_first_page; -} diff --git a/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_32.cpp b/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_32.cpp index 52f307686ae..5e91ce654f8 100644 --- a/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_32.cpp +++ b/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_32.cpp @@ -58,7 +58,3 @@ void MacroAssembler::get_thread(Register thread) { "Thread Pointer Offset has not been initialized"); movl(thread, Address(thread, ThreadLocalStorage::get_thread_ptr_offset())); } - -bool MacroAssembler::needs_explicit_null_check(intptr_t offset) { - return offset < 0 || (int)os::vm_page_size() <= offset; -} diff --git a/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_64.cpp b/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_64.cpp index d0abc969080..7ff190fb21b 100644 --- a/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_64.cpp +++ b/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_64.cpp @@ -65,19 +65,3 @@ void MacroAssembler::get_thread(Register thread) { popq(rax); } } - -bool MacroAssembler::needs_explicit_null_check(intptr_t offset) { - // Exception handler checks the nmethod's implicit null checks table - // only when this method returns false. - if (UseCompressedOops) { - // The first page after heap_base is unmapped and - // the 'offset' is equal to [heap_base + offset] for - // narrow oop implicit null checks. - uintptr_t heap_base = (uintptr_t)Universe::heap_base(); - if ((uintptr_t)offset >= heap_base) { - // Normalize offset for the next check. - offset = (intptr_t)(pointer_delta((void*)offset, (void*)heap_base, 1)); - } - } - return offset < 0 || os::vm_page_size() <= offset; -} diff --git a/hotspot/src/share/vm/asm/assembler.cpp b/hotspot/src/share/vm/asm/assembler.cpp index 62c9d232f72..dcf1f0dcd94 100644 --- a/hotspot/src/share/vm/asm/assembler.cpp +++ b/hotspot/src/share/vm/asm/assembler.cpp @@ -246,6 +246,24 @@ void AbstractAssembler::block_comment(const char* comment) { } } +bool MacroAssembler::needs_explicit_null_check(intptr_t offset) { + // Exception handler checks the nmethod's implicit null checks table + // only when this method returns false. +#ifndef SPARC + // Sparc does not have based addressing + if (UseCompressedOops) { + // The first page after heap_base is unmapped and + // the 'offset' is equal to [heap_base + offset] for + // narrow oop implicit null checks. + uintptr_t heap_base = (uintptr_t)Universe::heap_base(); + if ((uintptr_t)offset >= heap_base) { + // Normalize offset for the next check. + offset = (intptr_t)(pointer_delta((void*)offset, (void*)heap_base, 1)); + } + } +#endif // SPARC + return offset < 0 || os::vm_page_size() <= offset; +} #ifndef PRODUCT void Label::print_instructions(MacroAssembler* masm) const { diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp index c6723714344..204bf39171b 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp @@ -61,6 +61,8 @@ ParMarkBitMap::initialize(MemRegion covered_region) if (_virtual_space != NULL) { delete _virtual_space; _virtual_space = NULL; + // Release memory reserved in the space. + rs.release(); } return false; } diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp index 59542f94bfe..6814abf6b44 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp @@ -108,8 +108,8 @@ jint ParallelScavengeHeap::initialize() { // size than is needed or wanted for the perm gen. Use the "compound // alignment" ReservedSpace ctor to avoid having to use the same page size for // all gens. - ReservedSpace heap_rs(pg_max_size, pg_align, og_max_size + yg_max_size, - og_align); + ReservedHeapSpace heap_rs(pg_max_size, pg_align, og_max_size + yg_max_size, + og_align); os::trace_page_sizes("ps perm", pg_min_size, pg_max_size, pg_page_sz, heap_rs.base(), pg_max_size); os::trace_page_sizes("ps main", og_min_size + yg_min_size, diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp index ae516b74518..7bfbdb53978 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp @@ -422,6 +422,8 @@ ParallelCompactData::create_vspace(size_t count, size_t element_size) return vspace; } delete vspace; + // Release memory reserved in the space. + rs.release(); } return 0; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp index 912f5414cc9..001f579e5d2 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp @@ -71,13 +71,8 @@ bool PSVirtualSpace::contains(void* p) const { void PSVirtualSpace::release() { DEBUG_ONLY(PSVirtualSpaceVerifier this_verifier(this)); - if (reserved_low_addr() != NULL) { - if (special()) { - os::release_memory_special(reserved_low_addr(), reserved_size()); - } else { - (void)os::release_memory(reserved_low_addr(), reserved_size()); - } - } + // This may not release memory it didn't reserve. + // Use rs.release() to release the underlying memory instead. _reserved_low_addr = _reserved_high_addr = NULL; _committed_low_addr = _committed_high_addr = NULL; _special = false; diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.cpp b/hotspot/src/share/vm/memory/genCollectedHeap.cpp index 3548137abd5..e77e86a0096 100644 --- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp +++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp @@ -222,8 +222,8 @@ char* GenCollectedHeap::allocate(size_t alignment, *_total_reserved = total_reserved; *_n_covered_regions = n_covered_regions; - *heap_rs = ReservedSpace(total_reserved, alignment, - UseLargePages, heap_address); + *heap_rs = ReservedHeapSpace(total_reserved, alignment, + UseLargePages, heap_address); return heap_address; } diff --git a/hotspot/src/share/vm/prims/jni.cpp b/hotspot/src/share/vm/prims/jni.cpp index 8d75b762b3e..49ace5c9823 100644 --- a/hotspot/src/share/vm/prims/jni.cpp +++ b/hotspot/src/share/vm/prims/jni.cpp @@ -2173,8 +2173,7 @@ static char* get_bad_address() { size_t size = os::vm_allocation_granularity(); bad_address = os::reserve_memory(size); if (bad_address != NULL) { - os::commit_memory(bad_address, size); - os::protect_memory(bad_address, size); + os::protect_memory(bad_address, size, os::MEM_PROT_READ); } } return bad_address; diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index 421eae2cf0b..b5d35226c0d 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -1176,9 +1176,7 @@ void Arguments::set_ergonomics_flags() { // by ergonomics. if (MaxHeapSize <= max_heap_for_compressed_oops()) { if (FLAG_IS_DEFAULT(UseCompressedOops)) { - // Leave compressed oops off by default. Uncomment - // the following line to return it to default status. - // FLAG_SET_ERGO(bool, UseCompressedOops, true); + FLAG_SET_ERGO(bool, UseCompressedOops, true); } } else { if (UseCompressedOops && !FLAG_IS_DEFAULT(UseCompressedOops)) { diff --git a/hotspot/src/share/vm/runtime/os.cpp b/hotspot/src/share/vm/runtime/os.cpp index 27febbee046..f276e7e7250 100644 --- a/hotspot/src/share/vm/runtime/os.cpp +++ b/hotspot/src/share/vm/runtime/os.cpp @@ -922,8 +922,9 @@ void os::serialize_thread_states() { // time and expensive page trap spinning, 'SerializePageLock' is used to block // the mutator thread if such case is encountered. See bug 6546278 for details. Thread::muxAcquire(&SerializePageLock, "serialize_thread_states"); - os::protect_memory( (char *)os::get_memory_serialize_page(), os::vm_page_size() ); - os::unguard_memory( (char *)os::get_memory_serialize_page(), os::vm_page_size() ); + os::protect_memory((char *)os::get_memory_serialize_page(), + os::vm_page_size(), MEM_PROT_READ, /*is_committed*/true ); + os::unguard_memory((char *)os::get_memory_serialize_page(), os::vm_page_size()); Thread::muxRelease(&SerializePageLock); } diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp index 41b7ed80e03..0b8cea57884 100644 --- a/hotspot/src/share/vm/runtime/os.hpp +++ b/hotspot/src/share/vm/runtime/os.hpp @@ -193,7 +193,11 @@ class os: AllStatic { static bool commit_memory(char* addr, size_t size, size_t alignment_hint); static bool uncommit_memory(char* addr, size_t bytes); static bool release_memory(char* addr, size_t bytes); - static bool protect_memory(char* addr, size_t bytes); + + enum ProtType { MEM_PROT_NONE, MEM_PROT_READ, MEM_PROT_RW, MEM_PROT_RWX }; + static bool protect_memory(char* addr, size_t bytes, ProtType prot, + bool is_committed = false); + static bool guard_memory(char* addr, size_t bytes); static bool unguard_memory(char* addr, size_t bytes); static char* map_memory(int fd, const char* file_name, size_t file_offset, diff --git a/hotspot/src/share/vm/runtime/virtualspace.cpp b/hotspot/src/share/vm/runtime/virtualspace.cpp index 23b75dc9dd4..44471632df6 100644 --- a/hotspot/src/share/vm/runtime/virtualspace.cpp +++ b/hotspot/src/share/vm/runtime/virtualspace.cpp @@ -28,12 +28,15 @@ // ReservedSpace ReservedSpace::ReservedSpace(size_t size) { - initialize(size, 0, false, NULL); + initialize(size, 0, false, NULL, 0); } ReservedSpace::ReservedSpace(size_t size, size_t alignment, - bool large, char* requested_address) { - initialize(size, alignment, large, requested_address); + bool large, + char* requested_address, + const size_t noaccess_prefix) { + initialize(size+noaccess_prefix, alignment, large, requested_address, + noaccess_prefix); } char * @@ -105,7 +108,8 @@ char* ReservedSpace::reserve_and_align(const size_t reserve_size, ReservedSpace::ReservedSpace(const size_t prefix_size, const size_t prefix_align, const size_t suffix_size, - const size_t suffix_align) + const size_t suffix_align, + const size_t noaccess_prefix) { assert(prefix_size != 0, "sanity"); assert(prefix_align != 0, "sanity"); @@ -118,12 +122,16 @@ ReservedSpace::ReservedSpace(const size_t prefix_size, assert((suffix_align & prefix_align - 1) == 0, "suffix_align not divisible by prefix_align"); + // Add in noaccess_prefix to prefix_size; + const size_t adjusted_prefix_size = prefix_size + noaccess_prefix; + const size_t size = adjusted_prefix_size + suffix_size; + // On systems where the entire region has to be reserved and committed up // front, the compound alignment normally done by this method is unnecessary. const bool try_reserve_special = UseLargePages && prefix_align == os::large_page_size(); if (!os::can_commit_large_page_memory() && try_reserve_special) { - initialize(prefix_size + suffix_size, prefix_align, true); + initialize(size, prefix_align, true, NULL, noaccess_prefix); return; } @@ -131,15 +139,19 @@ ReservedSpace::ReservedSpace(const size_t prefix_size, _size = 0; _alignment = 0; _special = false; + _noaccess_prefix = 0; + + // Assert that if noaccess_prefix is used, it is the same as prefix_align. + assert(noaccess_prefix == 0 || + noaccess_prefix == prefix_align, "noaccess prefix wrong"); // Optimistically try to reserve the exact size needed. - const size_t size = prefix_size + suffix_size; char* addr = os::reserve_memory(size, NULL, prefix_align); if (addr == NULL) return; // Check whether the result has the needed alignment (unlikely unless // prefix_align == suffix_align). - const size_t ofs = size_t(addr) + prefix_size & suffix_align - 1; + const size_t ofs = size_t(addr) + adjusted_prefix_size & suffix_align - 1; if (ofs != 0) { // Wrong alignment. Release, allocate more space and do manual alignment. // @@ -153,11 +165,11 @@ ReservedSpace::ReservedSpace(const size_t prefix_size, } const size_t extra = MAX2(ofs, suffix_align - ofs); - addr = reserve_and_align(size + extra, prefix_size, prefix_align, + addr = reserve_and_align(size + extra, adjusted_prefix_size, prefix_align, suffix_size, suffix_align); if (addr == NULL) { // Try an even larger region. If this fails, address space is exhausted. - addr = reserve_and_align(size + suffix_align, prefix_size, + addr = reserve_and_align(size + suffix_align, adjusted_prefix_size, prefix_align, suffix_size, suffix_align); } } @@ -165,10 +177,12 @@ ReservedSpace::ReservedSpace(const size_t prefix_size, _base = addr; _size = size; _alignment = prefix_align; + _noaccess_prefix = noaccess_prefix; } void ReservedSpace::initialize(size_t size, size_t alignment, bool large, - char* requested_address) { + char* requested_address, + const size_t noaccess_prefix) { const size_t granularity = os::vm_allocation_granularity(); assert((size & granularity - 1) == 0, "size not aligned to os::vm_allocation_granularity()"); @@ -181,6 +195,7 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large, _size = 0; _special = false; _alignment = 0; + _noaccess_prefix = 0; if (size == 0) { return; } @@ -220,7 +235,8 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large, // important. If available space is not detected, return NULL. if (requested_address != 0) { - base = os::attempt_reserve_memory_at(size, requested_address); + base = os::attempt_reserve_memory_at(size, + requested_address-noaccess_prefix); } else { base = os::reserve_memory(size, NULL, alignment); } @@ -259,6 +275,11 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large, _base = base; _size = size; _alignment = MAX2(alignment, (size_t) os::vm_page_size()); + _noaccess_prefix = noaccess_prefix; + + // Assert that if noaccess_prefix is used, it is the same as alignment. + assert(noaccess_prefix == 0 || + noaccess_prefix == _alignment, "noaccess prefix wrong"); assert(markOopDesc::encode_pointer_as_mark(_base)->decode_pointer() == _base, "area must be distinguisable from marks for mark-sweep"); @@ -274,6 +295,7 @@ ReservedSpace::ReservedSpace(char* base, size_t size, size_t alignment, _base = base; _size = size; _alignment = alignment; + _noaccess_prefix = 0; _special = special; } @@ -320,17 +342,58 @@ size_t ReservedSpace::allocation_align_size_down(size_t size) { void ReservedSpace::release() { if (is_reserved()) { + char *real_base = _base - _noaccess_prefix; + const size_t real_size = _size + _noaccess_prefix; if (special()) { - os::release_memory_special(_base, _size); + os::release_memory_special(real_base, real_size); } else{ - os::release_memory(_base, _size); + os::release_memory(real_base, real_size); } _base = NULL; _size = 0; + _noaccess_prefix = 0; _special = false; } } +void ReservedSpace::protect_noaccess_prefix(const size_t size) { + // If there is noaccess prefix, return. + if (_noaccess_prefix == 0) return; + + assert(_noaccess_prefix >= (size_t)os::vm_page_size(), + "must be at least page size big"); + + // Protect memory at the base of the allocated region. + // If special, the page was committed (only matters on windows) + if (!os::protect_memory(_base, _noaccess_prefix, os::MEM_PROT_NONE, + _special)) { + fatal("cannot protect protection page"); + } + + _base += _noaccess_prefix; + _size -= _noaccess_prefix; + assert((size == _size) && ((uintptr_t)_base % _alignment == 0), + "must be exactly of required size and alignment"); +} + +ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, + bool large, char* requested_address) : + ReservedSpace(size, alignment, large, + requested_address, + UseCompressedOops ? lcm(os::vm_page_size(), alignment) : 0) { + // Only reserved space for the java heap should have a noaccess_prefix + // if using compressed oops. + protect_noaccess_prefix(size); +} + +ReservedHeapSpace::ReservedHeapSpace(const size_t prefix_size, + const size_t prefix_align, + const size_t suffix_size, + const size_t suffix_align) : + ReservedSpace(prefix_size, prefix_align, suffix_size, suffix_align, + UseCompressedOops ? lcm(os::vm_page_size(), prefix_align) : 0) { + protect_noaccess_prefix(prefix_size+suffix_size); +} // VirtualSpace @@ -348,6 +411,7 @@ VirtualSpace::VirtualSpace() { _lower_alignment = 0; _middle_alignment = 0; _upper_alignment = 0; + _special = false; } @@ -402,7 +466,8 @@ VirtualSpace::~VirtualSpace() { void VirtualSpace::release() { - (void)os::release_memory(low_boundary(), reserved_size()); + // This does not release memory it never reserved. + // Caller must release via rs.release(); _low_boundary = NULL; _high_boundary = NULL; _low = NULL; diff --git a/hotspot/src/share/vm/runtime/virtualspace.hpp b/hotspot/src/share/vm/runtime/virtualspace.hpp index ad952e49a3d..556b4c92bb6 100644 --- a/hotspot/src/share/vm/runtime/virtualspace.hpp +++ b/hotspot/src/share/vm/runtime/virtualspace.hpp @@ -29,13 +29,15 @@ class ReservedSpace VALUE_OBJ_CLASS_SPEC { private: char* _base; size_t _size; + size_t _noaccess_prefix; size_t _alignment; bool _special; // ReservedSpace ReservedSpace(char* base, size_t size, size_t alignment, bool special); void initialize(size_t size, size_t alignment, bool large, - char* requested_address = NULL); + char* requested_address, + const size_t noaccess_prefix); // Release parts of an already-reserved memory region [addr, addr + len) to // get a new region that has "compound alignment." Return the start of the @@ -59,13 +61,19 @@ class ReservedSpace VALUE_OBJ_CLASS_SPEC { const size_t suffix_size, const size_t suffix_align); + protected: + // Create protection page at the beginning of the space. + void protect_noaccess_prefix(const size_t size); + public: // Constructor ReservedSpace(size_t size); ReservedSpace(size_t size, size_t alignment, bool large, - char* requested_address = NULL); + char* requested_address = NULL, + const size_t noaccess_prefix = 0); ReservedSpace(const size_t prefix_size, const size_t prefix_align, - const size_t suffix_size, const size_t suffix_align); + const size_t suffix_size, const size_t suffix_align, + const size_t noaccess_prefix); // Accessors char* base() const { return _base; } @@ -73,6 +81,8 @@ class ReservedSpace VALUE_OBJ_CLASS_SPEC { size_t alignment() const { return _alignment; } bool special() const { return _special; } + size_t noaccess_prefix() const { return _noaccess_prefix; } + bool is_reserved() const { return _base != NULL; } void release(); @@ -104,6 +114,16 @@ ReservedSpace ReservedSpace::last_part(size_t partition_size) return last_part(partition_size, alignment()); } +// Class encapsulating behavior specific of memory space reserved for Java heap +class ReservedHeapSpace : public ReservedSpace { +public: + // Constructor + ReservedHeapSpace(size_t size, size_t forced_base_alignment, + bool large, char* requested_address); + ReservedHeapSpace(const size_t prefix_size, const size_t prefix_align, + const size_t suffix_size, const size_t suffix_align); +}; + // VirtualSpace is data structure for committing a previously reserved address range in smaller chunks. class VirtualSpace VALUE_OBJ_CLASS_SPEC { From 034e883c863a40a8cbbe1e8460beabbcb80cf502 Mon Sep 17 00:00:00 2001 From: Kelly O'Hair Date: Sun, 27 Jul 2008 18:42:57 -0700 Subject: [PATCH 20/21] 6727683: Cleanup use of COMPILER_WARNINGS_FATAL in makefiles Reviewed-by: tbell --- jdk/make/com/sun/java/pack/Makefile | 3 - .../com/sun/security/auth/module/Makefile | 3 - jdk/make/common/Defs-linux.gmk | 5 +- jdk/make/common/Defs-solaris.gmk | 38 +++++- jdk/make/common/Defs-windows.gmk | 23 ++-- jdk/make/common/shared/Compiler-gcc.gmk | 27 ++--- jdk/make/common/shared/Defs-java.gmk | 10 +- jdk/make/common/shared/Platform.gmk | 112 ++++++++++-------- jdk/make/java/fdlibm/Makefile | 2 - jdk/make/java/hpi/windows/Makefile | 2 - jdk/make/java/java/Makefile | 2 - jdk/make/java/java_crw_demo/Makefile | 5 - jdk/make/java/java_hprof_demo/Makefile | 5 - jdk/make/java/jli/Makefile | 3 - jdk/make/java/net/Makefile | 2 - jdk/make/java/nio/Makefile | 1 - jdk/make/java/npt/Makefile | 5 - jdk/make/java/verify/Makefile | 2 - jdk/make/java/zip/Makefile | 3 - jdk/make/jpda/back/Makefile | 5 - jdk/make/jpda/transport/shmem/Makefile | 7 -- jdk/make/jpda/transport/socket/Makefile | 5 - jdk/make/sun/cmm/kcms/Makefile | 2 - jdk/make/sun/font/Makefile | 3 - jdk/make/sun/font/t2k/Makefile | 3 - jdk/make/sun/jdbc/Makefile | 5 - jdk/make/sun/jpeg/Makefile | 5 - 27 files changed, 127 insertions(+), 161 deletions(-) diff --git a/jdk/make/com/sun/java/pack/Makefile b/jdk/make/com/sun/java/pack/Makefile index 0a230f7364f..9229e3f4c99 100644 --- a/jdk/make/com/sun/java/pack/Makefile +++ b/jdk/make/com/sun/java/pack/Makefile @@ -97,9 +97,6 @@ ifeq ($(PLATFORM), windows) /D "J2SE_FTYPE=0x1L" RES = $(OBJDIR)/$(PGRM).res - - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false else LDOUTPUT = -o #Have a space LDDFLAGS += -lc diff --git a/jdk/make/com/sun/security/auth/module/Makefile b/jdk/make/com/sun/security/auth/module/Makefile index 303c8475163..d9f705d4df7 100644 --- a/jdk/make/com/sun/security/auth/module/Makefile +++ b/jdk/make/com/sun/security/auth/module/Makefile @@ -55,9 +55,6 @@ LIBRARY = jaas_nt EXTRA_LIBS += netapi32.lib user32.lib mpr.lib endif #fdlibm # code generates errors when compiled at warning level 3 and warnings are fatal - ifeq ($(ARCH_DATA_MODEL), 64) - COMPILER_WARNINGS_FATAL=false - endif # ARCH_DATA_MODEL endif # windows ifeq ($(PLATFORM), solaris) diff --git a/jdk/make/common/Defs-linux.gmk b/jdk/make/common/Defs-linux.gmk index c4f1856f5fc..65814ba472a 100644 --- a/jdk/make/common/Defs-linux.gmk +++ b/jdk/make/common/Defs-linux.gmk @@ -149,10 +149,9 @@ endif # ARCH PIC_CODE_LARGE = -fPIC PIC_CODE_SMALL = -fpic GLOBAL_KPIC = $(PIC_CODE_LARGE) +CFLAGS_COMMON += $(GLOBAL_KPIC) $(GCC_WARNINGS) ifeq ($(ARCH), amd64) - CFLAGS_COMMON += $(GLOBAL_KPIC) $(GCC_WARNINGS) -pipe -else - CFLAGS_COMMON += $(GLOBAL_KPIC) $(GCC_WARNINGS) + CFLAGS_COMMON += -pipe endif # Linux 64bit machines use Dwarf2, which can be HUGE, have fastdebug use -g1 diff --git a/jdk/make/common/Defs-solaris.gmk b/jdk/make/common/Defs-solaris.gmk index 01ea5bd271f..d0874e43070 100644 --- a/jdk/make/common/Defs-solaris.gmk +++ b/jdk/make/common/Defs-solaris.gmk @@ -40,6 +40,9 @@ # LDLIBS (set $(EXTRA_LIBS) instead) # LDLIBS_COMMON (set $(EXTRA_LIBS) instead) # LINTFLAGS (set $(OTHER_LINTFLAGS) instead) +# +# Note: CPPFLAGS are used in C and C++ compiles. +# # Get shared JDK settings include $(JDK_MAKE_SHARED_DIR)/Defs.gmk @@ -112,6 +115,10 @@ endif # Required with many of the source files. # -mt Assume multi-threaded (important) # +# The more unusual options to the Sun C compiler: +# +w Print more warnings +# +w2 Maximum warnings +# # # Debug flag for C and C++ compiler @@ -140,15 +147,34 @@ ifeq ($(FASTDEBUG), true) CXXFLAGS_DEBUG_OPTION = -g0 $(CC_FASTDEBUG_OPT) endif -CFLAGS_COMMON = -v -mt -L$(OBJDIR) -xc99=%none +CFLAGS_COMMON = -L$(OBJDIR) + +# Do not allow C99 language features like declarations in code etc. +CFLAGS_COMMON += -xc99=%none + +# Allow C++ comments in C code CFLAGS_COMMON += -xCC -CFLAGS_COMMON += -errshort=tags + +# Show error message tags on errors +CFLAGS_COMMON += -errshort=tags +CXXFLAGS_COMMON += -errtags=yes + +# Optimization flags CFLAGS_OPT = $(POPT) + +# Debug version flags CFLAGS_DBG = $(CFLAGS_DEBUG_OPTION) -CFLAGS_COMMON += -Xa $(CFLAGS_REQUIRED) + +# Required C compiler flags +CFLAGS_COMMON += -Xa $(CFLAGS_REQUIRED) + +# Maximum warnings all the time +CXXFLAGS_COMMON += +w +CFLAGS_COMMON += -v # Assume MT behavior all the time (important) -CXXFLAGS_COMMON = -mt +CXXFLAGS_COMMON += -mt +CFLAGS_COMMON += -mt # Assume no C++ exceptions are used CXXFLAGS_COMMON += -features=no%except -DCC_NOEX @@ -237,8 +263,8 @@ LINTFLAGS_COMMON += $(LINT_XARCH_OPTION) # OTHER_CFLAGS += -DPERTURBALOT # -CPPFLAGS_COMMON = -D$(ARCH_FAMILY) -D__solaris__ -D_REENTRANT -CPPFLAGS_OPT = +CPPFLAGS_COMMON = -D__solaris__ -D$(ARCH_FAMILY) +CPPFLAGS_OPT = -DNDEBUG CPPFLAGS_DBG = -DDEBUG ifeq ($(ARCH_FAMILY), i586) diff --git a/jdk/make/common/Defs-windows.gmk b/jdk/make/common/Defs-windows.gmk index 51982dc1a1b..d7c11837e96 100644 --- a/jdk/make/common/Defs-windows.gmk +++ b/jdk/make/common/Defs-windows.gmk @@ -283,7 +283,7 @@ CPPFLAGS_COMMON = -DWIN32 -DIAL -D_LITTLE_ENDIAN ifeq ($(ARCH), amd64) CPPFLAGS_COMMON += -D_AMD64_ -Damd64 else - CPPFLAGS_COMMON += -DWIN32 -D_X86_ -Dx86 + CPPFLAGS_COMMON += -D_X86_ -Dx86 endif CPPFLAGS_COMMON += -DWIN32_LEAN_AND_MEAN @@ -292,17 +292,24 @@ CPPFLAGS_COMMON += -DWIN32_LEAN_AND_MEAN # CFLAGS_COMMON += -Fd$(OBJDIR)/$(basename $(@F)).pdb -Fm$(OBJDIR)/$(basename $(@F)).map +# +# Use -wdNNNN to disable warning NNNN. +# C4800 is a warning about bool performance casts (can't make go away) +# +COMPILER_WARNINGS_TO_IGNORE = 4800 +CFLAGS_COMMON += $(COMPILER_WARNINGS_TO_IGNORE:%=-wd%) + # # Add warnings and extra on 64bit issues # ifeq ($(ARCH_DATA_MODEL), 64) CFLAGS_COMMON += -Wp64 endif -CFLAGS_COMMON += -W$(COMPILER_WARNING_LEVEL) # # Treat compiler warnings as errors, if requested # +CFLAGS_COMMON += -W$(COMPILER_WARNING_LEVEL) ifeq ($(COMPILER_WARNINGS_FATAL),true) CFLAGS_COMMON += -WX endif @@ -352,17 +359,9 @@ else # BUILD_WIN_SA=1 # on the make command. ifdef BUILD_WIN_SA - ifeq ($(ARCH), amd64) - INCLUDE_SA = true - else - INCLUDE_SA = true - endif + INCLUDE_SA = true else - ifeq ($(ARCH), amd64) - INCLUDE_SA = false - else - INCLUDE_SA = false - endif + INCLUDE_SA = false endif endif diff --git a/jdk/make/common/shared/Compiler-gcc.gmk b/jdk/make/common/shared/Compiler-gcc.gmk index 2eca25a5a98..27501956368 100644 --- a/jdk/make/common/shared/Compiler-gcc.gmk +++ b/jdk/make/common/shared/Compiler-gcc.gmk @@ -73,23 +73,18 @@ ifeq ($(PLATFORM), linux) REQUIRED_CC_VER = 4.0 REQUIRED_GCC_VER = 4.0.* else - ifeq ($(ARCH_DATA_MODEL), 32) - # i586 REQUIRED_CC_VER = 3.2 - REQUIRED_GCC_VER = 3.2.1* - REQUIRED_GCC_VER_INT = 3.2.1-7a - else - ifeq ($(ARCH), amd64) - # amd64 - REQUIRED_CC_VER = 3.2 - REQUIRED_GCC_VER = 3.2.* - endif - ifeq ($(ARCH), ia64) - # ia64 - REQUIRED_CC_VER = 3.2 - REQUIRED_GCC_VER = 2.9[56789].* - endif - endif + ifeq ($(ARCH_DATA_MODEL), 32) + REQUIRED_GCC_VER = 3.2.1* + REQUIRED_GCC_VER_INT = 3.2.1-7a + else + ifeq ($(ARCH), amd64) + REQUIRED_GCC_VER = 3.2.* + endif + ifeq ($(ARCH), ia64) + REQUIRED_GCC_VER = 2.9[56789].* + endif + endif endif # Option used to create a shared library SHARED_LIBRARY_FLAG = -shared -mimpure-text diff --git a/jdk/make/common/shared/Defs-java.gmk b/jdk/make/common/shared/Defs-java.gmk index b3e02702783..179e53a01a9 100644 --- a/jdk/make/common/shared/Defs-java.gmk +++ b/jdk/make/common/shared/Defs-java.gmk @@ -107,7 +107,10 @@ JAVACFLAGS = ifeq ($(DEBUG_CLASSFILES),true) JAVACFLAGS += -g endif -ifeq ($(COMPILER_WARNINGS_FATAL), true) +ifeq ($(JAVAC_MAX_WARNINGS), true) + JAVACFLAGS += -Xlint:all +endif +ifeq ($(JAVAC_WARNINGS_FATAL), true) JAVACFLAGS += -Werror endif @@ -180,7 +183,10 @@ endif # The javac options supplied to the boot javac is limited. This compiler # should only be used to build the 'make/tools' sources, which are not # class files that end up in the classes directory. -ifeq ($(COMPILER_WARNINGS_FATAL), true) +ifeq ($(JAVAC_MAX_WARNINGS), true) + BOOT_JAVACFLAGS += -Xlint:all +endif +ifeq ($(JAVAC_WARNINGS_FATAL), true) BOOT_JAVACFLAGS += -Werror endif BOOT_JAVACFLAGS += -encoding ascii diff --git a/jdk/make/common/shared/Platform.gmk b/jdk/make/common/shared/Platform.gmk index abf93964332..e9859dd4bad 100644 --- a/jdk/make/common/shared/Platform.gmk +++ b/jdk/make/common/shared/Platform.gmk @@ -373,34 +373,40 @@ ifeq ($(PLATFORM), windows) REQUIRED_DXSDK_VER = 0x0700 OS_VENDOR = Microsoft # How much RAM does this machine have: - ifeq ($(USING_CYGWIN),true) - # CYGWIN has the 'free' utility - _MB_OF_MEMORY := \ - $(shell free -m | grep Mem: | awk '{print $$2;}' ) - else - # Windows 2000 has the mem utility, but two memory areas - # extended memory is what is beyond 1024M - _B_OF_EXT_MEMORY := \ - $(shell mem 2> $(DEV_NULL) | grep 'total contiguous extended memory' | awk '{print $$1;}') - ifeq ($(_B_OF_EXT_MEMORY),) - _B_OF_MEMORY := \ - $(shell mem 2> $(DEV_NULL) | grep 'total conventional memory' | awk '{print $$1;}') - else - _B_OF_MEMORY := \ - $(shell expr 1048576 '+' $(_B_OF_EXT_MEMORY) 2> $(DEV_NULL)) - endif - ifeq ($(_B_OF_MEMORY),) - # Windows 2003 has the systeminfo utility use it if mem doesn't work + ifeq ($(JDK_HAS_MEM_INFO),) + ifeq ($(USING_CYGWIN),true) + # CYGWIN has the 'free' utility _MB_OF_MEMORY := \ - $(shell systeminfo 2> $(DEV_NULL) | grep 'Total Physical Memory:' | awk '{print $$4;}' | sed -e 's@,@@') + $(shell free -m | grep Mem: | awk '{print $$2;}' ) else - _MB_OF_MEMORY := $(shell expr $(_B_OF_MEMORY) '/' 1024 2> $(DEV_NULL)) + # Windows 2000 has the mem utility, but two memory areas + # extended memory is what is beyond 1024M + _B_OF_EXT_MEMORY := \ + $(shell mem 2> $(DEV_NULL) | \ + grep 'total contiguous extended memory' | awk '{print $$1;}') + ifeq ($(_B_OF_EXT_MEMORY),) + _B_OF_MEMORY := \ + $(shell mem 2> $(DEV_NULL) | \ + grep 'total conventional memory' | awk '{print $$1;}') + else + _B_OF_MEMORY := \ + $(shell expr 1048576 '+' $(_B_OF_EXT_MEMORY) 2> $(DEV_NULL)) + endif + ifeq ($(_B_OF_MEMORY),) + # Windows 2003 has the systeminfo utility use it if mem doesn't work + _MB_OF_MEMORY := \ + $(shell systeminfo 2> $(DEV_NULL) | \ + grep 'Total Physical Memory:' | \ + awk '{print $$4;}' | sed -e 's@,@@') + else + _MB_OF_MEMORY := $(shell expr $(_B_OF_MEMORY) '/' 1024 2> $(DEV_NULL)) + endif + endif + ifeq ($(shell expr $(_MB_OF_MEMORY) '+' 0 2> $(DEV_NULL)), $(_MB_OF_MEMORY)) + MB_OF_MEMORY := $(_MB_OF_MEMORY) + else + MB_OF_MEMORY := 512 endif - endif - ifeq ($(shell expr $(_MB_OF_MEMORY) '+' 0 2> $(DEV_NULL)), $(_MB_OF_MEMORY)) - MB_OF_MEMORY := $(_MB_OF_MEMORY) - else - MB_OF_MEMORY := 512 endif endif @@ -446,30 +452,38 @@ endif # system swapping during the build. # If we don't know, assume 512. Subtract 128 from MB for VM MAX. # Don't set VM max over 1024-128=896. -ifneq ($(MB_OF_MEMORY),) - LOW_MEMORY_MACHINE := $(shell \ - if [ $(MB_OF_MEMORY) -le 512 ] ; then \ - echo "true"; \ - else \ - echo "false"; \ - fi) - MAX_VM_MEMORY := $(shell \ - if [ $(MB_OF_MEMORY) -le 1024 ] ; then \ - expr $(MB_OF_MEMORY) '-' 128 2> $(DEV_NULL) ; \ - else \ - echo "896"; \ - fi) - MIN_VM_MEMORY := $(shell \ - if [ $(MAX_VM_MEMORY) -le 128 ] ; then \ - expr $(MAX_VM_MEMORY) '-' 8 2> $(DEV_NULL) ; \ - else \ - echo "128"; \ - fi) -else - MB_OF_MEMORY := unknown - LOW_MEMORY_MACHINE := true - MAX_VM_MEMORY := 384 - MIN_VM_MEMORY := 128 +ifeq ($(JDK_HAS_MEM_INFO),) + JDK_HAS_MEM_INFO=true + export JDK_HAS_MEM_INFO + ifneq ($(MB_OF_MEMORY),) + LOW_MEMORY_MACHINE := $(shell \ + if [ $(MB_OF_MEMORY) -le 512 ] ; then \ + echo "true"; \ + else \ + echo "false"; \ + fi) + MAX_VM_MEMORY := $(shell \ + if [ $(MB_OF_MEMORY) -le 1024 ] ; then \ + expr $(MB_OF_MEMORY) '-' 128 2> $(DEV_NULL) ; \ + else \ + echo "896"; \ + fi) + MIN_VM_MEMORY := $(shell \ + if [ $(MAX_VM_MEMORY) -le 128 ] ; then \ + expr $(MAX_VM_MEMORY) '-' 8 2> $(DEV_NULL) ; \ + else \ + echo "128"; \ + fi) + else + MB_OF_MEMORY := unknown + LOW_MEMORY_MACHINE := true + MAX_VM_MEMORY := 384 + MIN_VM_MEMORY := 128 + endif + export MB_OF_MEMORY + export LOW_MEMORY_MACHINE + export MAX_VM_MEMORY + export MIN_VM_MEMORY endif # If blanks in the username, use the first 4 words and pack them together diff --git a/jdk/make/java/fdlibm/Makefile b/jdk/make/java/fdlibm/Makefile index fd2442b0321..ab7a411579c 100644 --- a/jdk/make/java/fdlibm/Makefile +++ b/jdk/make/java/fdlibm/Makefile @@ -46,8 +46,6 @@ ifeq ($(PLATFORM),windows) _OPT = $(CC_NO_OPT) OTHER_CFLAGS = CPPFLAGS_DBG += -DLOGGING - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false endif # diff --git a/jdk/make/java/hpi/windows/Makefile b/jdk/make/java/hpi/windows/Makefile index da163368e21..fa04ec6a362 100644 --- a/jdk/make/java/hpi/windows/Makefile +++ b/jdk/make/java/hpi/windows/Makefile @@ -37,8 +37,6 @@ include $(BUILDDIR)/common/Defs.gmk # windows compiler flags ifeq ($(PLATFORM),windows) CPPFLAGS_DBG += -DLOGGING - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false endif FILES_c = \ diff --git a/jdk/make/java/java/Makefile b/jdk/make/java/java/Makefile index 671bff3cf1a..8755a4c3a6e 100644 --- a/jdk/make/java/java/Makefile +++ b/jdk/make/java/java/Makefile @@ -37,8 +37,6 @@ include $(BUILDDIR)/common/Defs.gmk # windows compiler flags ifeq ($(PLATFORM),windows) OTHER_CFLAGS = - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false # build directly into BINDIR... LIB_LOCATION = $(BINDIR) # Exported functions diff --git a/jdk/make/java/java_crw_demo/Makefile b/jdk/make/java/java_crw_demo/Makefile index 2157e2f892c..c65a84df1e8 100644 --- a/jdk/make/java/java_crw_demo/Makefile +++ b/jdk/make/java/java_crw_demo/Makefile @@ -47,11 +47,6 @@ FILES_c = java_crw_demo.c OTHER_INCLUDES = -I$(SRCDIR) -# -# This removes all asserts in the optimized version -# -CPPFLAGS_OPT += -DNDEBUG - # # Library to compile. # diff --git a/jdk/make/java/java_hprof_demo/Makefile b/jdk/make/java/java_hprof_demo/Makefile index 71529433b14..9c97a6a24e5 100644 --- a/jdk/make/java/java_hprof_demo/Makefile +++ b/jdk/make/java/java_hprof_demo/Makefile @@ -91,11 +91,6 @@ endif # INIT += $(LIBDIR)/jvm.hprof.txt -# -# This removes all asserts in the optimized version -# -CPPFLAGS_OPT += -DNDEBUG - # # This puts logging code in # diff --git a/jdk/make/java/jli/Makefile b/jdk/make/java/jli/Makefile index 65adb05e248..5a1c312d7ab 100644 --- a/jdk/make/java/jli/Makefile +++ b/jdk/make/java/jli/Makefile @@ -115,9 +115,6 @@ ifeq ($(PLATFORM), windows) -export:JLI_ManifestIterate \ -export:JLI_SetTraceLauncher - # Files from zlib built here do not compile with warning level 3 - # if warnings are fatal - COMPILER_WARNINGS_FATAL=false endif OTHER_INCLUDES += -I$(LAUNCHER_SHARE_SRC) diff --git a/jdk/make/java/net/Makefile b/jdk/make/java/net/Makefile index 6218c63776e..4141294c02c 100644 --- a/jdk/make/java/net/Makefile +++ b/jdk/make/java/net/Makefile @@ -94,8 +94,6 @@ include $(BUILDDIR)/common/Library.gmk ifeq ($(PLATFORM), windows) OTHER_LDLIBS = ws2_32.lib $(JVMLIB) - # Will not compile at warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false else OTHER_LDLIBS = $(LIBSOCKET) -lnsl -ldl $(JVMLIB) endif diff --git a/jdk/make/java/nio/Makefile b/jdk/make/java/nio/Makefile index 9d083dab535..267a5745435 100644 --- a/jdk/make/java/nio/Makefile +++ b/jdk/make/java/nio/Makefile @@ -134,7 +134,6 @@ ifeq ($(PLATFORM),windows) $(OBJDIR)/../../../java.lang/java/$(OBJDIRNAME)/FileDescriptor_md.obj endif ifeq ($(PLATFORM), linux) -COMPILER_WARNINGS_FATAL=true OTHER_LDLIBS += -L$(LIBDIR)/$(LIBARCH) -ljava -lnet -lpthread -ldl endif ifeq ($(PLATFORM), solaris) diff --git a/jdk/make/java/npt/Makefile b/jdk/make/java/npt/Makefile index 48cab86ae07..fe9eb0ad4e3 100644 --- a/jdk/make/java/npt/Makefile +++ b/jdk/make/java/npt/Makefile @@ -52,11 +52,6 @@ FILES_c = \ OTHER_INCLUDES = -I$(SRCDIR) -I$(PSRCDIR) -# -# This removes all asserts in the optimized version -# -CPPFLAGS_OPT += -DNDEBUG - # # Library to compile. # diff --git a/jdk/make/java/verify/Makefile b/jdk/make/java/verify/Makefile index a90862d516a..c647d508569 100644 --- a/jdk/make/java/verify/Makefile +++ b/jdk/make/java/verify/Makefile @@ -43,8 +43,6 @@ ifeq ($(PLATFORM), windows) # JAVALIB = EXTRA_LIBS = - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false endif # diff --git a/jdk/make/java/zip/Makefile b/jdk/make/java/zip/Makefile index e25e46f1e4a..00b381d5085 100644 --- a/jdk/make/java/zip/Makefile +++ b/jdk/make/java/zip/Makefile @@ -49,9 +49,6 @@ FILES_export = \ ifneq ($(PLATFORM), windows) OTHER_CFLAGS += -DUSE_MMAP -else - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false endif # diff --git a/jdk/make/jpda/back/Makefile b/jdk/make/jpda/back/Makefile index a974bff608b..bd9818365e3 100644 --- a/jdk/make/jpda/back/Makefile +++ b/jdk/make/jpda/back/Makefile @@ -52,11 +52,6 @@ ifneq ($(PLATFORM), windows) OTHER_LDLIBS += -ldl endif # PLATFORM -# -# This turns off all assert() checking in the optimized library -# -CPPFLAGS_OPT += -DNDEBUG - # # This controls the ability to do logging in the library. # diff --git a/jdk/make/jpda/transport/shmem/Makefile b/jdk/make/jpda/transport/shmem/Makefile index ad5ff4b3729..a1e2382500a 100644 --- a/jdk/make/jpda/transport/shmem/Makefile +++ b/jdk/make/jpda/transport/shmem/Makefile @@ -36,13 +36,6 @@ FILES_m = mapfile-vers include $(BUILDDIR)/common/Defs.gmk -# 64-bit windows does not build at -W3 if warnings are fatal -ifeq ($(PLATFORM), windows) - ifeq ($(ARCH_DATA_MODEL), 64) - COMPILER_WARNINGS_FATAL=false - endif -endif - FILES_c = \ SharedMemoryTransport.c \ SharedMemoryConnection.c \ diff --git a/jdk/make/jpda/transport/socket/Makefile b/jdk/make/jpda/transport/socket/Makefile index 07c5642a2d3..828613d49ed 100644 --- a/jdk/make/jpda/transport/socket/Makefile +++ b/jdk/make/jpda/transport/socket/Makefile @@ -36,11 +36,6 @@ FILES_m = mapfile-vers include $(BUILDDIR)/common/Defs.gmk -ifeq ($(PLATFORM), windows) - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false -endif - ifeq ($(PLATFORM), linux) OTHER_LDLIBS += -lnsl $(LIBSOCKET) -lpthread endif diff --git a/jdk/make/sun/cmm/kcms/Makefile b/jdk/make/sun/cmm/kcms/Makefile index b430a140716..000f2527e5b 100644 --- a/jdk/make/sun/cmm/kcms/Makefile +++ b/jdk/make/sun/cmm/kcms/Makefile @@ -47,8 +47,6 @@ FILES_export = \ ifeq ($(PLATFORM), windows) # Override the default version info with our own resource file (see 5043594) VERSIONINFO_RESOURCE = $(CLOSED_SRC)/share/native/sun/java2d/cmm/kcms/cmm.rc - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false endif # Rules diff --git a/jdk/make/sun/font/Makefile b/jdk/make/sun/font/Makefile index def2eba8057..a247bb21eb5 100644 --- a/jdk/make/sun/font/Makefile +++ b/jdk/make/sun/font/Makefile @@ -77,9 +77,6 @@ FILES_export = \ ifeq ($(PLATFORM), windows) - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false - LDLIBS += user32.lib gdi32.lib $(OBJDIR)/../../../sun.awt/awt/$(OBJDIRNAME)/awt.lib OTHER_CFLAGS += -DCC_NOEX diff --git a/jdk/make/sun/font/t2k/Makefile b/jdk/make/sun/font/t2k/Makefile index 93d2e59d29b..65f7f99a4dd 100644 --- a/jdk/make/sun/font/t2k/Makefile +++ b/jdk/make/sun/font/t2k/Makefile @@ -64,9 +64,6 @@ FILES_export = \ ifeq ($(PLATFORM), windows) - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false - # t2k imports several shared methods from fontmanager.dll LDLIBS += user32.lib $(OBJDIR)/../../../sun.font/fontmanager/$(OBJDIRNAME)/fontmanager.lib diff --git a/jdk/make/sun/jdbc/Makefile b/jdk/make/sun/jdbc/Makefile index d520b22cf85..818a89d0e8b 100644 --- a/jdk/make/sun/jdbc/Makefile +++ b/jdk/make/sun/jdbc/Makefile @@ -69,11 +69,6 @@ ifneq ($(PLATFORM), windows) INIT += $(ODBC_FAKE_LIBRARIES) endif -ifeq ($(PLATFORM),windows) - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false -endif - # # Rules # diff --git a/jdk/make/sun/jpeg/Makefile b/jdk/make/sun/jpeg/Makefile index 45bc70855dd..e3513763316 100644 --- a/jdk/make/sun/jpeg/Makefile +++ b/jdk/make/sun/jpeg/Makefile @@ -73,10 +73,5 @@ include $(BUILDDIR)/common/Library.gmk # vpath %.c $(SHARE_SRC)/native/$(PKGDIR)/image/jpeg -ifeq ($(PLATFORM), windows) - # Files built here do not compile with warning level 3 if warnings are fatal - COMPILER_WARNINGS_FATAL=false -endif # PLATFORM - CLASSES.export += java.io.InputStream From 632c83fb46317a48bfb608e6c7ca415d5f38187d Mon Sep 17 00:00:00 2001 From: Erik Trimble Date: Fri, 1 Aug 2008 18:51:27 -0700 Subject: [PATCH 21/21] 6732819: Turn off compressed oops by default for now Workaround for CompOops bug Reviewed-by: coleenp --- hotspot/src/share/vm/runtime/arguments.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index b5d35226c0d..882e92b337d 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -1176,7 +1176,8 @@ void Arguments::set_ergonomics_flags() { // by ergonomics. if (MaxHeapSize <= max_heap_for_compressed_oops()) { if (FLAG_IS_DEFAULT(UseCompressedOops)) { - FLAG_SET_ERGO(bool, UseCompressedOops, true); + // Turn off until bug is fixed. + // FLAG_SET_ERGO(bool, UseCompressedOops, true); } } else { if (UseCompressedOops && !FLAG_IS_DEFAULT(UseCompressedOops)) {
    - - OpenJDK - + OpenJDK
    make.exe Develmake: The GNU version of the 'make' utilitymake: The GNU version of the 'make' utility
    + NOTE: See the GNU make section
    m4.execpio: A program to manage archives of files
    awk.exegawk.exe Utils awk: Pattern-directed scanning and processing language
    zip.exeUtilsArchive zip: Package and compress (archive) files
    unzip.exeUtilsArchive unzip: Extract compressed files in a ZIP archive
    free.exeUtilsProcps free: Display amount of free and used memory in the system