5108776: Add reliable event handling to the JMX API
authorsjiang
Thu Jul 31 15:31:13 2008 +0200 (15 months ago)
changeset 4428f52c4d1d934
parent 4417622f1de1486
child 44398caad5c563c
5108776: Add reliable event handling to the JMX API
6218920: API bug - impossible to delete last MBeanServerForwarder on a connector
Reviewed-by: emcmanus
src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java
src/share/classes/com/sun/jmx/event/EventBuffer.java
src/share/classes/com/sun/jmx/event/EventClientFactory.java
src/share/classes/com/sun/jmx/event/EventConnection.java
src/share/classes/com/sun/jmx/event/EventParams.java
src/share/classes/com/sun/jmx/event/LeaseManager.java
src/share/classes/com/sun/jmx/event/LeaseRenewer.java
src/share/classes/com/sun/jmx/event/ReceiverBuffer.java
src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java
src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java
src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java
src/share/classes/com/sun/jmx/interceptor/package.html
src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java
src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java
src/share/classes/com/sun/jmx/mbeanserver/PerThreadGroupPool.java
src/share/classes/com/sun/jmx/mbeanserver/Util.java
src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java
src/share/classes/com/sun/jmx/remote/internal/ProxyInputStream.java
src/share/classes/com/sun/jmx/remote/internal/ProxyRef.java
src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java
src/share/classes/com/sun/jmx/remote/security/FileLoginModule.java
src/share/classes/com/sun/jmx/remote/util/EnvHelp.java
src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java
src/share/classes/com/sun/jmx/snmp/tasks/ThreadService.java
src/share/classes/javax/management/ImmutableDescriptor.java
src/share/classes/javax/management/MBeanServer.java
src/share/classes/javax/management/MBeanServerConnection.java
src/share/classes/javax/management/MXBean.java
src/share/classes/javax/management/QueryParser.java
src/share/classes/javax/management/StringValueExp.java
src/share/classes/javax/management/event/EventClient.java
src/share/classes/javax/management/event/EventClientDelegate.java
src/share/classes/javax/management/event/EventClientDelegateMBean.java
src/share/classes/javax/management/event/EventClientNotFoundException.java
src/share/classes/javax/management/event/EventConsumer.java
src/share/classes/javax/management/event/EventForwarder.java
src/share/classes/javax/management/event/EventReceiver.java
src/share/classes/javax/management/event/EventRelay.java
src/share/classes/javax/management/event/EventSubscriber.java
src/share/classes/javax/management/event/FetchingEventForwarder.java
src/share/classes/javax/management/event/FetchingEventRelay.java
src/share/classes/javax/management/event/ListenerInfo.java
src/share/classes/javax/management/event/NotificationManager.java
src/share/classes/javax/management/event/RMIPushEventForwarder.java
src/share/classes/javax/management/event/RMIPushEventRelay.java
src/share/classes/javax/management/event/RMIPushServer.java
src/share/classes/javax/management/event/package-info.java
src/share/classes/javax/management/loading/MLet.java
src/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java
src/share/classes/javax/management/modelmbean/RequiredModelMBean.java
src/share/classes/javax/management/relation/RelationService.java
src/share/classes/javax/management/remote/IdentityMBeanServerForwarder.java
src/share/classes/javax/management/remote/JMXConnector.java
src/share/classes/javax/management/remote/JMXConnectorServer.java
src/share/classes/javax/management/remote/JMXConnectorServerFactory.java
src/share/classes/javax/management/remote/JMXConnectorServerMBean.java
src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java
src/share/classes/javax/management/remote/rmi/RMIConnector.java
src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java
test/javax/management/MBeanServer/DynamicWrapperMBeanTest.java
test/javax/management/MBeanServer/OldMBeanServerTest.java
test/javax/management/eventService/AddRemoveListenerTest.java
test/javax/management/eventService/CustomForwarderTest.java
test/javax/management/eventService/EventClientExecutorTest.java
test/javax/management/eventService/EventDelegateSecurityTest.java
test/javax/management/eventService/EventManagerTest.java
test/javax/management/eventService/FetchingTest.java
test/javax/management/eventService/LeaseManagerDeadlockTest.java
test/javax/management/eventService/LeaseTest.java
test/javax/management/eventService/ListenerTest.java
test/javax/management/eventService/MyFetchingEventForwarder.java
test/javax/management/eventService/NotSerializableNotifTest.java
test/javax/management/eventService/PublishTest.java
test/javax/management/eventService/ReconnectableConnectorTest.java
test/javax/management/eventService/SharingThreadTest.java
test/javax/management/eventService/SubscribeTest.java
test/javax/management/eventService/UsingEventService.java
test/javax/management/mxbean/GenericArrayTypeTest.java
test/javax/management/mxbean/LeakTest.java
test/javax/management/mxbean/MBeanOperationInfoTest.java
test/javax/management/mxbean/MXBeanTest.java
test/javax/management/mxbean/ThreadMXBeanTest.java
test/javax/management/mxbean/TigerMXBean.java
test/javax/management/query/QueryNotifFilterTest.java
test/javax/management/remote/mandatory/connection/CloseServerTest.java
test/javax/management/remote/mandatory/connection/DeadLockTest.java
test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java
test/javax/management/remote/mandatory/connection/RMIExitTest.java
test/javax/management/remote/mandatory/connection/ReconnectTest.java
test/javax/management/remote/mandatory/connectorServer/ForwarderChainTest.java
test/javax/management/remote/mandatory/connectorServer/StandardForwardersTest.java
test/javax/management/remote/mandatory/loading/MissingClassTest.java
test/javax/management/remote/mandatory/notif/AddRemoveTest.java
test/javax/management/remote/mandatory/notif/DiffHBTest.java
test/javax/management/remote/mandatory/notif/EmptyDomainNotificationTest.java
test/javax/management/remote/mandatory/notif/ListenerScaleTest.java
test/javax/management/remote/mandatory/notif/NotifBufferSizePropertyNameTest.java
test/javax/management/remote/mandatory/notif/NotifReconnectDeadlockTest.java
test/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java
test/javax/management/remote/mandatory/notif/NotificationBufferCreationTest.java
test/javax/management/remote/mandatory/notif/NotificationBufferDeadlockTest.java
test/javax/management/remote/mandatory/notif/NotificationEmissionTest.java
test/javax/management/remote/mandatory/notif/RMINotifTest.java
test/javax/management/remote/mandatory/notif/UnexpectedNotifTest.java
--- a/src/share/classes/com/sun/jmx/interceptor/package.html Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/com/sun/jmx/interceptor/package.html Thu Jul 31 15:31:13 2008 +0200
@@ -29,5 +29,8 @@ have any questions.
</head>
<body bgcolor="white">
Provides specific classes to <B>Sun JMX Reference Implementation</B>.
+ <p><b>
+ This API is a Sun internal API and is subject to changes without notice.
+ </b></p>
</BODY>
</HTML>
--- a/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java Thu Jul 31 15:31:13 2008 +0200
@@ -172,7 +172,7 @@ public class MBeanInjector {
* 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
+ * (a) its type is exactly ObjectName 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
--- a/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java Thu Jul 31 15:31:13 2008 +0200
@@ -25,7 +25,6 @@
package com.sun.jmx.mbeanserver;
-import static com.sun.jmx.mbeanserver.Util.*;
import javax.management.Attribute;
import javax.management.AttributeList;
--- a/src/share/classes/com/sun/jmx/mbeanserver/Util.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/Util.java Thu Jul 31 15:31:13 2008 +0200
@@ -38,10 +38,13 @@ import java.util.Map;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
+import java.util.SortedSet;
import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.WeakHashMap;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
+import javax.management.loading.ClassLoaderRepository;
public class Util {
static <K, V> Map<K, V> newMap() {
@@ -142,4 +145,97 @@ public class Util {
return hash;
}
+ /**
+ * Filters a set of ObjectName according to a given pattern.
+ *
+ * @param pattern the pattern that the returned names must match.
+ * @param all the set of names to filter.
+ * @return a set of ObjectName from which non matching names
+ * have been removed.
+ */
+ public static Set<ObjectName> filterMatchingNames(ObjectName pattern,
+ Set<ObjectName> all) {
+ // If no pattern, just return all names
+ if (pattern == null
+ || all.isEmpty()
+ || ObjectName.WILDCARD.equals(pattern))
+ return all;
+
+ // If there's a pattern, do the matching.
+ final Set<ObjectName> res = equivalentEmptySet(all);
+ for (ObjectName n : all) if (pattern.apply(n)) res.add(n);
+ return res;
+ }
+
+ /**
+ * An abstract ClassLoaderRepository that contains a single class loader.
+ **/
+ private final static class SingleClassLoaderRepository
+ implements ClassLoaderRepository {
+ private final ClassLoader singleLoader;
+
+ SingleClassLoaderRepository(ClassLoader loader) {
+ this.singleLoader = loader;
+ }
+
+ ClassLoader getSingleClassLoader() {
+ return singleLoader;
+ }
+
+ private Class<?> loadClass(String className, ClassLoader loader)
+ throws ClassNotFoundException {
+ return Class.forName(className, false, loader);
+ }
+
+ public Class<?> loadClass(String className)
+ throws ClassNotFoundException {
+ return loadClass(className, getSingleClassLoader());
+ }
+
+ public Class<?> loadClassWithout(ClassLoader exclude,
+ String className) throws ClassNotFoundException {
+ final ClassLoader loader = getSingleClassLoader();
+ if (exclude != null && exclude.equals(loader))
+ throw new ClassNotFoundException(className);
+ return loadClass(className, loader);
+ }
+
+ public Class<?> loadClassBefore(ClassLoader stop, String className)
+ throws ClassNotFoundException {
+ return loadClassWithout(stop, className);
+ }
+ }
+
+ /**
+ * Returns a ClassLoaderRepository that contains a single class loader.
+ * @param loader the class loader contained in the returned repository.
+ * @return a ClassLoaderRepository that contains the single loader.
+ */
+ public static ClassLoaderRepository getSingleClassLoaderRepository(
+ final ClassLoader loader) {
+ return new SingleClassLoaderRepository(loader);
+ }
+
+ public static <T> Set<T> cloneSet(Set<T> set) {
+ if (set instanceof SortedSet) {
+ @SuppressWarnings("unchecked")
+ SortedSet<T> sset = (SortedSet<T>) set;
+ set = new TreeSet<T>(sset.comparator());
+ set.addAll(sset);
+ } else
+ set = new HashSet<T>(set);
+ return set;
+ }
+
+ public static <T> Set<T> equivalentEmptySet(Set<T> set) {
+ if (set instanceof SortedSet) {
+ @SuppressWarnings("unchecked")
+ SortedSet<T> sset = (SortedSet<T>) set;
+ set = new TreeSet<T>(sset.comparator());
+ } else if (set != null) {
+ set = new HashSet<T>(set.size());
+ } else
+ set = new HashSet<T>();
+ return set;
+ }
}
--- a/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java Thu Jul 31 15:31:13 2008 +0200
@@ -576,6 +576,7 @@ public abstract class ClientNotifForward
int notFoundCount = 0;
NotificationResult result = null;
+ long firstEarliest = -1;
while (result == null && !shouldStop()) {
NotificationResult nr;
@@ -598,6 +599,8 @@ public abstract class ClientNotifForward
return null;
startSequenceNumber = nr.getNextSequenceNumber();
+ if (firstEarliest < 0)
+ firstEarliest = nr.getEarliestSequenceNumber();
try {
// 1 notif to skip possible missing class
@@ -628,6 +631,17 @@ public abstract class ClientNotifForward
(notFoundCount == 1 ? "" : "s") +
" because classes were missing locally";
lostNotifs(msg, notFoundCount);
+ // Even if result.getEarliestSequenceNumber() is now greater than
+ // it was initially, meaning some notifs have been dropped
+ // from the buffer, we don't want the caller to see that
+ // because it is then likely to renotify about the lost notifs.
+ // So we put back the first value of earliestSequenceNumber
+ // that we saw.
+ if (result != null) {
+ result = new NotificationResult(
+ firstEarliest, result.getNextSequenceNumber(),
+ result.getTargetedNotifications());
+ }
}
return result;
--- a/src/share/classes/com/sun/jmx/remote/internal/ProxyInputStream.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/com/sun/jmx/remote/internal/ProxyInputStream.java Thu Jul 31 15:31:13 2008 +0200
@@ -33,10 +33,8 @@ import org.omg.CORBA.Context;
import org.omg.CORBA.Context;
import org.omg.CORBA.NO_IMPLEMENT;
import org.omg.CORBA.ORB;
-import org.omg.CORBA.Principal;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.portable.BoxedValueHelper;
-import org.omg.CORBA_2_3.portable.InputStream;
@SuppressWarnings("deprecation")
public class ProxyInputStream extends org.omg.CORBA_2_3.portable.InputStream {
@@ -160,54 +158,71 @@ public class ProxyInputStream extends or
return in.read_any();
}
- public Principal read_Principal() {
+ /**
+ * @deprecated
+ */
+ @Override
+ @Deprecated
+ public org.omg.CORBA.Principal read_Principal() {
return in.read_Principal();
}
+ @Override
public int read() throws IOException {
return in.read();
}
+ @Override
public BigDecimal read_fixed() {
return in.read_fixed();
}
+ @Override
public Context read_Context() {
return in.read_Context();
}
+ @Override
public org.omg.CORBA.Object read_Object(java.lang.Class clz) {
return in.read_Object(clz);
}
+ @Override
public ORB orb() {
return in.orb();
}
+ @Override
public Serializable read_value() {
return narrow().read_value();
}
+ @Override
public Serializable read_value(Class clz) {
return narrow().read_value(clz);
}
+ @Override
public Serializable read_value(BoxedValueHelper factory) {
return narrow().read_value(factory);
}
+ @Override
public Serializable read_value(String rep_id) {
return narrow().read_value(rep_id);
}
+ @Override
public Serializable read_value(Serializable value) {
return narrow().read_value(value);
}
+ @Override
public Object read_abstract_interface() {
return narrow().read_abstract_interface();
}
+ @Override
public Object read_abstract_interface(Class clz) {
return narrow().read_abstract_interface(clz);
}
--- a/src/share/classes/com/sun/jmx/remote/internal/ProxyRef.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/com/sun/jmx/remote/internal/ProxyRef.java Thu Jul 31 15:31:13 2008 +0200
@@ -31,8 +31,6 @@ import java.lang.reflect.Method;
import java.lang.reflect.Method;
import java.rmi.Remote;
import java.rmi.RemoteException;
-import java.rmi.server.Operation;
-import java.rmi.server.RemoteCall;
import java.rmi.server.RemoteObject;
import java.rmi.server.RemoteRef;
@@ -54,7 +52,11 @@ public class ProxyRef implements RemoteR
ref.writeExternal(out);
}
- public void invoke(RemoteCall call) throws Exception {
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ public void invoke(java.rmi.server.RemoteCall call) throws Exception {
ref.invoke(call);
}
@@ -63,7 +65,11 @@ public class ProxyRef implements RemoteR
return ref.invoke(obj, method, params, opnum);
}
- public void done(RemoteCall call) throws RemoteException {
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ public void done(java.rmi.server.RemoteCall call) throws RemoteException {
ref.done(call);
}
@@ -71,7 +77,12 @@ public class ProxyRef implements RemoteR
return ref.getRefClass(out);
}
- public RemoteCall newCall(RemoteObject obj, Operation[] op, int opnum,
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ public java.rmi.server.RemoteCall newCall(RemoteObject obj,
+ java.rmi.server.Operation[] op, int opnum,
long hash) throws RemoteException {
return ref.newCall(obj, op, opnum, hash);
}
--- a/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java Thu Jul 31 15:31:13 2008 +0200
@@ -25,16 +25,16 @@
package com.sun.jmx.remote.internal;
+import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.remote.security.NotificationAccessController;
import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -67,9 +67,9 @@ public class ServerNotifForwarder {
connectionTimeout = EnvHelp.getServerConnectionTimeout(env);
checkNotificationEmission = EnvHelp.computeBooleanFromString(
env,
- "jmx.remote.x.check.notification.emission");
- notificationAccessController = (NotificationAccessController)
- env.get("com.sun.jmx.remote.notification.access.controller");
+ "jmx.remote.x.check.notification.emission",false);
+ notificationAccessController =
+ EnvHelp.getNotificationAccessController(env);
}
public Integer addNotificationListener(final ObjectName name,
@@ -88,9 +88,7 @@ public class ServerNotifForwarder {
checkMBeanPermission(name, "addNotificationListener");
if (notificationAccessController != null) {
notificationAccessController.addNotificationListener(
- connectionId,
- name,
- Subject.getSubject(AccessController.getContext()));
+ connectionId, name, getSubject());
}
try {
boolean instanceOf =
@@ -160,9 +158,7 @@ public class ServerNotifForwarder {
checkMBeanPermission(name, "removeNotificationListener");
if (notificationAccessController != null) {
notificationAccessController.removeNotificationListener(
- connectionId,
- name,
- Subject.getSubject(AccessController.getContext()));
+ connectionId, name, getSubject());
}
Exception re = null;
@@ -312,6 +308,10 @@ public class ServerNotifForwarder {
// PRIVATE METHODS
//----------------
+ private Subject getSubject() {
+ return Subject.getSubject(AccessController.getContext());
+ }
+
private void checkState() throws IOException {
synchronized(terminationLock) {
if (terminated) {
@@ -332,7 +332,13 @@ public class ServerNotifForwarder {
*/
private void checkMBeanPermission(final ObjectName name,
final String actions)
- throws InstanceNotFoundException, SecurityException {
+ throws InstanceNotFoundException, SecurityException {
+ checkMBeanPermission(mbeanServer, name, actions);
+ }
+
+ public static void checkMBeanPermission(
+ final MBeanServer mbs, final ObjectName name, final String actions)
+ throws InstanceNotFoundException, SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
AccessControlContext acc = AccessController.getContext();
@@ -342,7 +348,7 @@ public class ServerNotifForwarder {
new PrivilegedExceptionAction<ObjectInstance>() {
public ObjectInstance run()
throws InstanceNotFoundException {
- return mbeanServer.getObjectInstance(name);
+ return mbs.getObjectInstance(name);
}
});
} catch (PrivilegedActionException e) {
@@ -364,14 +370,12 @@ public class ServerNotifForwarder {
TargetedNotification tn) {
try {
if (checkNotificationEmission) {
- checkMBeanPermission(name, "addNotificationListener");
+ checkMBeanPermission(
+ name, "addNotificationListener");
}
if (notificationAccessController != null) {
notificationAccessController.fetchNotification(
- connectionId,
- name,
- tn.getNotification(),
- Subject.getSubject(AccessController.getContext()));
+ connectionId, name, tn.getNotification(), getSubject());
}
return true;
} catch (SecurityException e) {
--- a/src/share/classes/com/sun/jmx/remote/security/FileLoginModule.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/com/sun/jmx/remote/security/FileLoginModule.java Thu Jul 31 15:31:13 2008 +0200
@@ -25,6 +25,7 @@
package com.sun.jmx.remote.security;
+import com.sun.jmx.mbeanserver.GetPropertyAction;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -46,8 +47,6 @@ import com.sun.jmx.remote.util.ClassLogg
import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;
import sun.management.jmxremote.ConnectorBootstrap;
-
-import sun.security.action.GetPropertyAction;
/**
* This {@link LoginModule} performs file-based authentication.
@@ -479,7 +478,7 @@ public class FileLoginModule implements
if (userSuppliedPasswordFile || hasJavaHomePermission) {
throw e;
} else {
- FilePermission fp =
+ final FilePermission fp =
new FilePermission(passwordFileDisplayName, "read");
AccessControlException ace = new AccessControlException(
"access denied " + fp.toString());
@@ -488,10 +487,13 @@ public class FileLoginModule implements
}
}
try {
- BufferedInputStream bis = new BufferedInputStream(fis);
- userCredentials = new Properties();
- userCredentials.load(bis);
- bis.close();
+ final BufferedInputStream bis = new BufferedInputStream(fis);
+ try {
+ userCredentials = new Properties();
+ userCredentials.load(bis);
+ } finally {
+ bis.close();
+ }
} finally {
fis.close();
}
--- a/src/share/classes/com/sun/jmx/remote/util/EnvHelp.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/com/sun/jmx/remote/util/EnvHelp.java Thu Jul 31 15:31:13 2008 +0200
@@ -40,9 +40,6 @@ import java.util.TreeSet;
import java.util.TreeSet;
import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
import javax.management.ObjectName;
import javax.management.MBeanServer;
@@ -50,6 +47,9 @@ import javax.management.remote.JMXConnec
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXConnectorServerFactory;
import com.sun.jmx.mbeanserver.GetPropertyAction;
+import com.sun.jmx.remote.security.NotificationAccessController;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorServer;
public class EnvHelp {
@@ -346,7 +346,24 @@ public class EnvHelp {
*/
public static long getFetchTimeout(Map env) {
return getIntegerAttribute(env, FETCH_TIMEOUT, 60000L, 0,
- Long.MAX_VALUE);
+ Long.MAX_VALUE);
+ }
+
+ /**
+ * <p>Name of the attribute that specifies an object that will check
+ * accesses to add/removeNotificationListener and also attempts to
+ * receive notifications. The value associated with this attribute
+ * should be a <code>NotificationAccessController</code> object.
+ * The default value is null.</p>
+ * This field is not public because of its com.sun dependency.
+ */
+ public static final String NOTIF_ACCESS_CONTROLLER =
+ "com.sun.jmx.remote.notification.access.controller";
+
+ public static NotificationAccessController getNotificationAccessController(
+ Map env) {
+ return (env == null) ? null :
+ (NotificationAccessController) env.get(NOTIF_ACCESS_CONTROLLER);
}
/**
@@ -470,24 +487,24 @@ public class EnvHelp {
}
/**
- The value of this attribute, if present, is a string specifying
- what other attributes should not appear in
- JMXConnectorServer.getAttributes(). It is a space-separated
- list of attribute patterns, where each pattern is either an
- attribute name, or an attribute prefix followed by a "*"
- character. The "*" has no special significance anywhere except
- at the end of a pattern. By default, this list is added to the
- list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which
- uses the same format). If the value of this attribute begins
- with an "=", then the remainder of the string defines the
- complete list of attribute patterns.
+ * The value of this attribute, if present, is a string specifying
+ * what other attributes should not appear in
+ * JMXConnectorServer.getAttributes(). It is a space-separated
+ * list of attribute patterns, where each pattern is either an
+ * attribute name, or an attribute prefix followed by a "*"
+ * character. The "*" has no special significance anywhere except
+ * at the end of a pattern. By default, this list is added to the
+ * list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which
+ * uses the same format). If the value of this attribute begins
+ * with an "=", then the remainder of the string defines the
+ * complete list of attribute patterns.
*/
public static final String HIDDEN_ATTRIBUTES =
"jmx.remote.x.hidden.attributes";
/**
- Default list of attributes not to show.
- @see #HIDDEN_ATTRIBUTES
+ * Default list of attributes not to show.
+ * @see #HIDDEN_ATTRIBUTES
*/
/* This list is copied directly from the spec, plus
java.naming.security.*. Most of the attributes here would have
@@ -651,6 +668,8 @@ public class EnvHelp {
* @param env the environment map.
* @param prop the name of the property in the environment map whose
* returned string value must be converted into a boolean value.
+ * @param systemProperty if true, consult a system property of the
+ * same name if there is no entry in the environment map.
*
* @return
* <ul>
@@ -671,16 +690,73 @@ public class EnvHelp {
* @throws ClassCastException if {@code env.get(prop)} cannot be cast
* to {@code String}.
*/
- public static boolean computeBooleanFromString(Map env, String prop)
- throws IllegalArgumentException, ClassCastException {
+ public static boolean computeBooleanFromString(
+ Map env, String prop, boolean systemProperty) {
if (env == null)
throw new IllegalArgumentException("env map cannot be null");
+ // returns a default value of 'false' if no property is found...
+ return computeBooleanFromString(env,prop,systemProperty,false);
+ }
+
+ /**
+ * Computes a boolean value from a string value retrieved from a
+ * property in the given map.
+ *
+ * @param env the environment map.
+ * @param prop the name of the property in the environment map whose
+ * returned string value must be converted into a boolean value.
+ * @param systemProperty if true, consult a system property of the
+ * same name if there is no entry in the environment map.
+ * @param defaultValue a default value to return in case no property
+ * was defined.
+ *
+ * @return
+ * <ul>
+ * <li>{@code defaultValue} if {@code env.get(prop)} is {@code null}
+ * and {@code systemProperty} is {@code false}</li>
+ * <li>{@code defaultValue} if {@code env.get(prop)} is {@code null}
+ * and {@code systemProperty} is {@code true} and
+ * {@code System.getProperty(prop)} is {@code null}</li>
+ * <li>{@code false} if {@code env.get(prop)} is {@code null}
+ * and {@code systemProperty} is {@code true} and
+ * {@code System.getProperty(prop).equalsIgnoreCase("false")}
+ * is {@code true}</li>
+ * <li>{@code true} if {@code env.get(prop)} is {@code null}
+ * and {@code systemProperty} is {@code true} and
+ * {@code System.getProperty(prop).equalsIgnoreCase("true")}
+ * is {@code true}</li>
+ * <li>{@code false} if
+ * {@code ((String)env.get(prop)).equalsIgnoreCase("false")}
+ * is {@code true}</li>
+ * <li>{@code true} if
+ * {@code ((String)env.get(prop)).equalsIgnoreCase("true")}
+ * is {@code true}</li>
+ * </ul>
+ *
+ * @throws IllegalArgumentException if {@code env} is {@code null} or
+ * {@code env.get(prop)} is not {@code null} and
+ * {@code ((String)env.get(prop)).equalsIgnoreCase("false")} and
+ * {@code ((String)env.get(prop)).equalsIgnoreCase("true")} are
+ * {@code false}.
+ * @throws ClassCastException if {@code env.get(prop)} cannot be cast
+ * to {@code String}.
+ */
+ public static boolean computeBooleanFromString(
+ Map env, String prop, boolean systemProperty, boolean defaultValue) {
+
+ if (env == null)
+ throw new IllegalArgumentException("env map cannot be null");
+
String stringBoolean = (String) env.get(prop);
+ if (stringBoolean == null && systemProperty) {
+ stringBoolean =
+ AccessController.doPrivileged(new GetPropertyAction(prop));
+ }
if (stringBoolean == null)
- return false;
+ return defaultValue;
else if (stringBoolean.equalsIgnoreCase("true"))
return true;
else if (stringBoolean.equalsIgnoreCase("false"))
@@ -703,6 +779,65 @@ public class EnvHelp {
return new Hashtable<K, V>(m);
}
+ /**
+ * Returns true if the parameter JMXConnector.USE_EVENT_SERVICE is set to a
+ * String equals "true" by ignoring case in the map or in the System.
+ */
+ public static boolean eventServiceEnabled(Map env) {
+ return computeBooleanFromString(env, JMXConnector.USE_EVENT_SERVICE, true);
+ }
+
+ /**
+ * Returns true if the parameter JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE
+ * is set to a String equals "true" (ignores case).
+ * If the property DELEGATE_TO_EVENT_SERVICE is not set, returns
+ * a default value of "true".
+ */
+ public static boolean delegateToEventService(Map env) {
+ return computeBooleanFromString(env,
+ JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE, true, true);
+ }
+
+// /**
+// * <p>Name of the attribute that specifies an EventRelay object to use.
+// */
+// public static final String EVENT_RELAY =
+// "jmx.remote.x.event.relay";
+//
+//
+// /**
+// * Returns an EventRelay object. The default one is FetchingEventRelay.
+// * If {@code EVENT_RELAY} is specified in {@code env} as a key,
+// * its value will be returned as an EventRelay object, if the value is
+// * not of type {@code EventRelay}, the default {@code FetchingEventRelay}
+// * will be returned.
+// * If {@code EVENT_RELAY} is not specified but {@code ENABLE_EVENT_RELAY}
+// * is specified as a key and its value is <code true>, the default {@code FetchingEventRelay}
+// * will be returned.
+// */
+// public static EventRelay getEventRelay(Map env) {
+// Map info = env == null ?
+// Collections.EMPTY_MAP : env;
+//
+// Object o = env.get(EVENT_RELAY);
+// if (o instanceof EventRelay) {
+// return (EventRelay)o;
+// } else if (o != null) {
+// logger.warning("getEventRelay",
+// "The user specified object is not an EventRelay object, " +
+// "using the default class FetchingEventRelay.");
+//
+// return new FetchingEventRelay();
+// }
+//
+// if (enableEventRelay(env)) {
+// return new FetchingEventRelay();
+// }
+//
+// return null;
+// }
+
+
private static final class SinkOutputStream extends OutputStream {
public void write(byte[] b, int off, int len) {}
public void write(int b) {}
--- a/src/share/classes/com/sun/jmx/snmp/tasks/ThreadService.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/com/sun/jmx/snmp/tasks/ThreadService.java Thu Jul 31 15:31:13 2008 +0200
@@ -45,15 +45,9 @@ public class ThreadService implements Ta
minThreads = threadNumber;
threadList = new ExecutorThread[threadNumber];
-// for (int i=0; i<threadNumber; i++) {
-// threadList[i] = new ExecutorThread();
-// threadList[i].start();
-// }
-
priority = Thread.currentThread().getPriority();
cloader = Thread.currentThread().getContextClassLoader();
-//System.out.println("---jsl: ThreadService: running threads = "+threadNumber);
}
// public methods
@@ -89,7 +83,6 @@ public class ThreadService implements Ta
synchronized(jobList) {
jobList.add(jobList.size(), task);
-//System.out.println("jsl-ThreadService: added job "+addedJobs++);
jobList.notify();
}
@@ -196,8 +189,6 @@ public class ThreadService implements Ta
try {
idle--;
job.run();
-//System.out.println("jsl-ThreadService: done job "+doneJobs++);
-
} catch (Exception e) {
// TODO
e.printStackTrace();
@@ -228,7 +219,6 @@ public class ThreadService implements Ta
ExecutorThread et = new ExecutorThread();
et.start();
threadList[currThreds++] = et;
-//System.out.println("jsl-ThreadService: create new thread: "+currThreds);
}
}
}
--- a/src/share/classes/javax/management/ImmutableDescriptor.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/ImmutableDescriptor.java Thu Jul 31 15:31:13 2008 +0200
@@ -128,13 +128,13 @@ public class ImmutableDescriptor impleme
* @throws InvalidObjectException if the read object has invalid fields.
*/
private Object readResolve() throws InvalidObjectException {
- if (names.length == 0 && getClass() == ImmutableDescriptor.class)
- return EMPTY_DESCRIPTOR;
boolean bad = false;
if (names == null || values == null || names.length != values.length)
bad = true;
if (!bad) {
+ if (names.length == 0 && getClass() == ImmutableDescriptor.class)
+ return EMPTY_DESCRIPTOR;
final Comparator<String> compare = String.CASE_INSENSITIVE_ORDER;
String lastName = ""; // also catches illegal null name
for (int i = 0; i < names.length; i++) {
--- a/src/share/classes/javax/management/MBeanServer.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/MBeanServer.java Thu Jul 31 15:31:13 2008 +0200
@@ -420,7 +420,13 @@ public interface MBeanServer extends MBe
// doc comment inherited from MBeanServerConnection
public String[] getDomains();
- // doc comment inherited from MBeanServerConnection
+ // doc comment inherited from MBeanServerConnection, plus:
+ /**
+ * {@inheritDoc}
+ * If the source of the notification
+ * is a reference to an MBean object, the MBean server will replace it
+ * by that MBean's ObjectName. Otherwise the source is unchanged.
+ */
public void addNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
--- a/src/share/classes/javax/management/MBeanServerConnection.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/MBeanServerConnection.java Thu Jul 31 15:31:13 2008 +0200
@@ -29,6 +29,7 @@ package javax.management;
// java import
import java.io.IOException;
import java.util.Set;
+import javax.management.event.NotificationManager;
/**
@@ -39,7 +40,7 @@ import java.util.Set;
*
* @since 1.5
*/
-public interface MBeanServerConnection {
+public interface MBeanServerConnection extends NotificationManager {
/**
* <p>Instantiates and registers an MBean in the MBean server. The
* MBean server will use its {@link
@@ -676,32 +677,7 @@ public interface MBeanServerConnection {
public String[] getDomains()
throws IOException;
- /**
- * <p>Adds a listener to a registered MBean.</p>
- *
- * <P> A notification emitted by an MBean will be forwarded by the
- * MBeanServer to the listener. If the source of the notification
- * is a reference to an MBean object, the MBean server will replace it
- * by that MBean's ObjectName. Otherwise the source is unchanged.
- *
- * @param name The name of the MBean on which the listener should
- * be added.
- * @param listener The listener object which will handle the
- * notifications emitted by the registered MBean.
- * @param filter The filter object. If filter is null, no
- * filtering will be performed before handling notifications.
- * @param handback The context to be sent to the listener when a
- * notification is emitted.
- *
- * @exception InstanceNotFoundException The MBean name provided
- * does not match any of the registered MBeans.
- * @exception IOException A communication problem occurred when
- * talking to the MBean server.
- *
- * @see #removeNotificationListener(ObjectName, NotificationListener)
- * @see #removeNotificationListener(ObjectName, NotificationListener,
- * NotificationFilter, Object)
- */
+ // doc inherited from NotificationManager
public void addNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
@@ -818,65 +794,13 @@ public interface MBeanServerConnection {
throws InstanceNotFoundException, ListenerNotFoundException,
IOException;
-
- /**
- * <p>Removes a listener from a registered MBean.</p>
- *
- * <P> If the listener is registered more than once, perhaps with
- * different filters or callbacks, this method will remove all
- * those registrations.
- *
- * @param name The name of the MBean on which the listener should
- * be removed.
- * @param listener The listener to be removed.
- *
- * @exception InstanceNotFoundException The MBean name provided
- * does not match any of the registered MBeans.
- * @exception ListenerNotFoundException The listener is not
- * registered in the MBean.
- * @exception IOException A communication problem occurred when
- * talking to the MBean server.
- *
- * @see #addNotificationListener(ObjectName, NotificationListener,
- * NotificationFilter, Object)
- */
+ // doc inherited from NotificationManager
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException;
- /**
- * <p>Removes a listener from a registered MBean.</p>
- *
- * <p>The MBean must have a listener that exactly matches the
- * given <code>listener</code>, <code>filter</code>, and
- * <code>handback</code> parameters. If there is more than one
- * such listener, only one is removed.</p>
- *
- * <p>The <code>filter</code> and <code>handback</code> parameters
- * may be null if and only if they are null in a listener to be
- * removed.</p>
- *
- * @param name The name of the MBean on which the listener should
- * be removed.
- * @param listener The listener to be removed.
- * @param filter The filter that was specified when the listener
- * was added.
- * @param handback The handback that was specified when the
- * listener was added.
- *
- * @exception InstanceNotFoundException The MBean name provided
- * does not match any of the registered MBeans.
- * @exception ListenerNotFoundException The listener is not
- * registered in the MBean, or it is not registered with the given
- * filter and handback.
- * @exception IOException A communication problem occurred when
- * talking to the MBean server.
- *
- * @see #addNotificationListener(ObjectName, NotificationListener,
- * NotificationFilter, Object)
- *
- */
+ // doc inherited from NotificationManager
public void removeNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
--- a/src/share/classes/javax/management/MXBean.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/MXBean.java Thu Jul 31 15:31:13 2008 +0200
@@ -33,7 +33,6 @@ import java.lang.annotation.Target;
import java.lang.annotation.Target;
// remaining imports are for Javadoc
-import java.beans.ConstructorProperties;
import java.io.InvalidObjectException;
import java.lang.management.MemoryUsage;
import java.lang.reflect.UndeclaredThrowableException;
@@ -865,7 +864,8 @@ public interface ModuleMXBean {
<em>J</em>.</p></li>
<li><p>Otherwise, if <em>J</em> has at least one public
- constructor with a {@link ConstructorProperties} annotation, then one
+ constructor with a {@link java.beans.ConstructorProperties
+ ConstructorProperties} annotation, then one
of those constructors (not necessarily always the same one)
will be called to reconstruct an instance of <em>J</em>.
Every such annotation must list as many strings as the
--- a/src/share/classes/javax/management/QueryParser.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/QueryParser.java Thu Jul 31 15:31:13 2008 +0200
@@ -312,7 +312,7 @@ class QueryParser {
if (e > 0)
ss = s.substring(0, e);
ss = ss.replace("0", "").replace(".", "");
- if (!ss.isEmpty())
+ if (!ss.equals(""))
throw new NumberFormatException("Underflow: " + s);
}
return d;
--- a/src/share/classes/javax/management/StringValueExp.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/StringValueExp.java Thu Jul 31 15:31:13 2008 +0200
@@ -85,6 +85,7 @@ public class StringValueExp implements V
/* There is no need for this method, because if a query is being
evaluated a StringValueExp can only appear inside a QueryExp,
and that QueryExp will itself have done setMBeanServer. */
+ @Deprecated
public void setMBeanServer(MBeanServer s) { }
/**
--- a/src/share/classes/javax/management/loading/MLet.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/loading/MLet.java Thu Jul 31 15:31:13 2008 +0200
@@ -1154,21 +1154,29 @@ public class MLet extends java.net.URLCl
*/
private synchronized String loadLibraryAsResource(String libname) {
try {
- InputStream is = getResourceAsStream(libname.replace(File.separatorChar,'/'));
+ InputStream is = getResourceAsStream(
+ libname.replace(File.separatorChar,'/'));
if (is != null) {
- File directory = new File(libraryDirectory);
- directory.mkdirs();
- File file = File.createTempFile(libname + ".", null, directory);
- file.deleteOnExit();
- FileOutputStream fileOutput = new FileOutputStream(file);
- int c;
- while ((c = is.read()) != -1) {
- fileOutput.write(c);
- }
- is.close();
- fileOutput.close();
- if (file.exists()) {
- return file.getAbsolutePath();
+ try {
+ File directory = new File(libraryDirectory);
+ directory.mkdirs();
+ File file = File.createTempFile(libname + ".", null,
+ directory);
+ file.deleteOnExit();
+ FileOutputStream fileOutput = new FileOutputStream(file);
+ try {
+ int c;
+ while ((c = is.read()) != -1) {
+ fileOutput.write(c);
+ }
+ } finally {
+ fileOutput.close();
+ }
+ if (file.exists()) {
+ return file.getAbsolutePath();
+ }
+ } finally {
+ is.close();
}
}
} catch (Exception e) {
--- a/src/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java Thu Jul 31 15:31:13 2008 +0200
@@ -373,7 +373,7 @@ public class ModelMBeanInfoSupport exten
"getDescriptors(String)", "Entry");
}
- if ((inDescriptorType == null) || (inDescriptorType.isEmpty())) {
+ if ((inDescriptorType == null) || (inDescriptorType.equals(""))) {
inDescriptorType = "all";
}
@@ -616,7 +616,7 @@ public class ModelMBeanInfoSupport exten
inDescriptor = new DescriptorSupport();
}
- if ((inDescriptorType == null) || (inDescriptorType.isEmpty())) {
+ if ((inDescriptorType == null) || (inDescriptorType.equals(""))) {
inDescriptorType =
(String) inDescriptor.getFieldValue("descriptorType");
--- a/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java Thu Jul 31 15:31:13 2008 +0200
@@ -1123,7 +1123,7 @@ public class RequiredModelMBean
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),"resolveMethod",
- "resolving " + targetClass + "." + opMethodName);
+ "resolving " + targetClass.getName() + "." + opMethodName);
}
final Class[] argClasses;
--- a/src/share/classes/javax/management/relation/RelationService.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/relation/RelationService.java Thu Jul 31 15:31:13 2008 +0200
@@ -108,7 +108,7 @@ public class RelationService extends Not
// the value HashMap mapping:
// <relation id> -> ArrayList of <role name>
// to track where a given MBean is referenced.
- private Map<ObjectName,Map<String,List<String>>>
+ private final Map<ObjectName,Map<String,List<String>>>
myRefedMBeanObjName2RelIdsMap =
new HashMap<ObjectName,Map<String,List<String>>>();
@@ -1492,7 +1492,7 @@ public class RelationService extends Not
// Clones the list of notifications to be able to still receive new
// notifications while proceeding those ones
List<MBeanServerNotification> localUnregNtfList;
- synchronized(myUnregNtfList) {
+ synchronized(myRefedMBeanObjName2RelIdsMap) {
localUnregNtfList =
new ArrayList<MBeanServerNotification>(myUnregNtfList);
// Resets list
--- a/src/share/classes/javax/management/remote/JMXConnector.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/remote/JMXConnector.java Thu Jul 31 15:31:13 2008 +0200
@@ -56,6 +56,26 @@ public interface JMXConnector extends Cl
*/
public static final String CREDENTIALS =
"jmx.remote.credentials";
+
+ /**
+ * <p>Name of the attribute that specifies whether to use the
+ * {@linkplain javax.management.event Event Service} to handle
+ * notifications for this connector. The value associated with
+ * this attribute, if any, is a String, which must be equal,
+ * ignoring case, to {@code "true"} or {@code "false"}.</p>
+ *
+ * <p>Not all connectors will understand this attribute, but the
+ * standard {@linkplain javax.management.remote.rmi.RMIConnector
+ * RMI Connector} does.</p>
+ *
+ * <p>If this attribute is not present, then the system property of the
+ * same name (<code>{@value}</code>) is consulted. If that is not set
+ * either, then the Event Service is not used.</p>
+ *
+ * @since 1.7
+ */
+ public static final String USE_EVENT_SERVICE =
+ "jmx.remote.use.event.service";
/**
* <p>Establishes the connection to the connector server. This
--- a/src/share/classes/javax/management/remote/JMXConnectorServer.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/remote/JMXConnectorServer.java Thu Jul 31 15:31:13 2008 +0200
@@ -26,17 +26,21 @@
package javax.management.remote;
+import com.sun.jmx.remote.util.EnvHelp;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
+import javax.management.MBeanInfo; // for javadoc
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.ObjectName;
+import javax.management.event.EventClientDelegate;
/**
* <p>Superclass of every connector server. A connector server is
@@ -75,6 +79,48 @@ public abstract class JMXConnectorServer
public static final String AUTHENTICATOR =
"jmx.remote.authenticator";
+ /**
+ * <p>Name of the attribute that specifies whether this connector
+ * server can delegate notification handling to the
+ * {@linkplain javax.management.event Event Service}.
+ * The value associated with
+ * this attribute, if any, is a String, which must be equal,
+ * ignoring case, to {@code "true"} or {@code "false"}.</p>
+ *
+ * <p>Not all connector servers will understand this attribute, but the
+ * standard {@linkplain javax.management.remote.rmi.RMIConnectorServer
+ * RMI Connector Server} does.</p>
+ *
+ * <p>If this attribute is not present, then the system property of the
+ * same name (<code>{@value}</code>) is consulted. If that is not set
+ * either, then the Event Service is used if the connector server
+ * supports it.</p>
+ *
+ * @since 1.7
+ */
+ public static final String DELEGATE_TO_EVENT_SERVICE =
+ "jmx.remote.delegate.event.service";
+
+ /**
+ * <p>Name of the attribute that specifies whether this connector
+ * server simulates the existence of the {@link EventClientDelegate}
+ * MBean. The value associated with this attribute, if any, must
+ * be a string that is equal to {@code "true"} or {@code "false"},
+ * ignoring case. If it is {@code "true"}, then the connector server
+ * will simulate an EventClientDelegate MBean, as described in {@link
+ * EventClientDelegate#newForwarder}. This MBean is needed for {@link
+ * javax.management.event.EventClient EventClient} to function correctly.</p>
+ *
+ * <p>Not all connector servers will understand this attribute, but the
+ * standard {@linkplain javax.management.remote.rmi.RMIConnectorServer
+ * RMI Connector Server} does. For a connector server that understands
+ * this attribute, the default value is {@code "true"}.</p>
+ *
+ * @since 1.7
+ */
+ public static final String EVENT_CLIENT_DELEGATE_FORWARDER =
+ "jmx.remote.event.client.delegate.forwarder";
+
/**
* <p>Constructs a connector server that will be registered as an
* MBean in the MBean server it is attached to. This constructor
@@ -89,34 +135,274 @@ public abstract class JMXConnectorServer
/**
* <p>Constructs a connector server that is attached to the given
* MBean server. A connector server that is created in this way
- * can be registered in a different MBean server.</p>
+ * can be registered in a different MBean server, or not registered
+ * in any MBean server.</p>
*
* @param mbeanServer the MBean server that this connector server
* is attached to. Null if this connector server will be attached
* to an MBean server by being registered in it.
*/
public JMXConnectorServer(MBeanServer mbeanServer) {
- this.mbeanServer = mbeanServer;
+ insertUserMBeanServer(mbeanServer);
}
/**
* <p>Returns the MBean server that this connector server is
- * attached to.</p>
+ * attached to, or the first in a chain of user-added
+ * {@link MBeanServerForwarder}s, if any.</p>
*
* @return the MBean server that this connector server is attached
* to, or null if it is not yet attached to an MBean server.
+ *
+ * @see #setMBeanServerForwarder
+ * @see #getSystemMBeanServer
*/
public synchronized MBeanServer getMBeanServer() {
- return mbeanServer;
- }
-
- public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf)
- {
+ return userMBeanServer;
+ }
+
+ public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
if (mbsf == null)
throw new IllegalArgumentException("Invalid null argument: mbsf");
- if (mbeanServer != null) mbsf.setMBeanServer(mbeanServer);
- mbeanServer = mbsf;
+ if (userMBeanServer != null)
+ mbsf.setMBeanServer(userMBeanServer);
+ insertUserMBeanServer(mbsf);
+ }
+
+ /**
+ * <p>Remove a forwarder from the chain of forwarders. The forwarder can
+ * be in the system chain or the user chain. On successful return from
+ * this method, the first occurrence in the chain of an object that is
+ * {@linkplain Object#equals equal} to {@code mbsf} will have been
+ * removed.</p>
+ * @param mbsf the forwarder to remove
+ * @throws NoSuchElementException if there is no occurrence of {@code mbsf}
+ * in the chain.
+ * @throws IllegalArgumentException if {@code mbsf} is null.
+ */
+ public synchronized void removeMBeanServerForwarder(MBeanServerForwarder mbsf) {
+ if (mbsf == null)
+ throw new IllegalArgumentException("Invalid null argument: mbsf");
+
+ MBeanServerForwarder prev = null;
+ MBeanServer curr = systemMBeanServer;
+ while (curr instanceof MBeanServerForwarder && !mbsf.equals(curr)) {
+ prev = (MBeanServerForwarder) curr;
+ curr = prev.getMBeanServer();
+ }
+ if (!(curr instanceof MBeanServerForwarder))
+ throw new NoSuchElementException("MBeanServerForwarder not in chain");
+ MBeanServerForwarder deleted = (MBeanServerForwarder) curr;
+ MBeanServer next = deleted.getMBeanServer();
+ if (prev != null)
+ prev.setMBeanServer(next);
+ if (systemMBeanServer == deleted)
+ systemMBeanServer = next;
+ if (userMBeanServer == deleted)
+ userMBeanServer = next;
+ }
+
+ /*
+ * Set userMBeanServer to mbs and arrange for the end of the chain of
+ * system MBeanServerForwarders to point to it. See the comment before
+ * the systemMBeanServer and userMBeanServer field declarations.
+ */
+ private void insertUserMBeanServer(MBeanServer mbs) {
+ MBeanServerForwarder lastSystemMBSF = null;
+ for (MBeanServer mbsi = systemMBeanServer;
+ mbsi != userMBeanServer;
+ mbsi = lastSystemMBSF.getMBeanServer()) {
+ lastSystemMBSF = (MBeanServerForwarder) mbsi;
+ }
+ userMBeanServer = mbs;
+ if (lastSystemMBSF == null)
+ systemMBeanServer = mbs;
+ else
+ lastSystemMBSF.setMBeanServer(mbs);
+ }
+
+ /**
+ * <p>Returns the first item in the chain of system and then user
+ * forwarders. In the simplest case, a {@code JMXConnectorServer}
+ * is connected directly to an {@code MBeanServer}. But there can
+ * also be a chain of {@link MBeanServerForwarder}s between the two.
+ * This chain consists of two sub-chains: first the <em>system chain</em>
+ * and then the <em>user chain</em>. Incoming requests are given to the
+ * first forwarder in the system chain. Each forwarder can handle
+ * a request itself, or more usually forward it to the next forwarder,
+ * perhaps with some extra behavior such as logging or security
+ * checking before or after the forwarding. The last forwarder in
+ * the system chain is followed by the first forwarder in the user
+ * chain.</p>
+ *
+ * <p>The <em>system chain</em> is usually
+ * defined by a connector server based on the environment Map;
+ * see {@link JMXConnectorServerFactory#newJMXConnectorServer}. Allowing the
+ * connector server to define its forwarders in this way ensures that
+ * they are in the correct order - some forwarders need to be inserted
+ * before others for correct behavior. It is possible to modify the
+ * system chain, for example using {@link #setSystemMBeanServerForwarder} or
+ * {@link #removeMBeanServerForwarder}, but in that case the system
+ * chain is no longer guaranteed to be correct.</p>
+ *
+ * <p>The <em>user chain</em> is defined by calling {@link
+ * #setMBeanServerForwarder} to insert forwarders at the head of the user
+ * chain.</p>
+ *
+ * <p>If there are no forwarders in either chain, then both
+ * {@link #getMBeanServer()} and {@code getSystemMBeanServer()} will
+ * return the {@code MBeanServer} for this connector server. If there
+ * are forwarders in the user chain but not the system chain, then
+ * both methods will return the first forwarder in the user chain.
+ * If there are forwarders in the system chain but not the user chain,
+ * then {@code getSystemMBeanServer()} will return the first forwarder
+ * in the system chain, and {@code getMBeanServer()} will return the
+ * {@code MBeanServer} for this connector server. Finally, if there
+ * are forwarders in each chain then {@code getSystemMBeanServer()}
+ * will return the first forwarder in the system chain, and {@code
+ * getMBeanServer()} will return the first forwarder in the user chain.</p>
+ *
+ * <p>This code illustrates how the chains can be traversed:</p>
+ *
+ * <pre>
+ * JMXConnectorServer cs;
+ * System.out.println("system chain:");
+ * MBeanServer mbs = cs.getSystemMBeanServer();
+ * while (true) {
+ * if (mbs == cs.getMBeanServer())
+ * System.out.println("user chain:");
+ * if (!(mbs instanceof MBeanServerForwarder))
+ * break;
+ * MBeanServerForwarder mbsf = (MBeanServerForwarder) mbs;
+ * System.out.println("--forwarder: " + mbsf);
+ * mbs = mbsf.getMBeanServer();
+ * }
+ * System.out.println("--MBean Server");
+ * </pre>
+ *
+ * @return the first item in the system chain of forwarders.
+ *
+ * @see #setSystemMBeanServerForwarder
+ */
+ public synchronized MBeanServer getSystemMBeanServer() {
+ return systemMBeanServer;
+ }
+
+ /**
+ * <p>Inserts an object that intercepts requests for the MBean server
+ * that arrive through this connector server. This object will be
+ * supplied as the <code>MBeanServer</code> for any new connection
+ * created by this connector server. Existing connections are
+ * unaffected.</p>
+ *
+ * <p>This method can be called more than once with different
+ * {@link MBeanServerForwarder} objects. The result is a chain
+ * of forwarders. The last forwarder added is the first in the chain.</p>
+ *
+ * <p>This method modifies the system chain of {@link MBeanServerForwarder}s.
+ * Usually user code should change the user chain instead, via
+ * {@link #setMBeanServerForwarder}.</p>
+ *
+ * <p>Not all connector servers support a system chain of forwarders.
+ * Calling this method on a connector server that does not will produce an
+ * {@link UnsupportedOperationException}.</p>
+ *
+ * <p>Suppose {@code mbs} is the result of {@link #getSystemMBeanServer()}
+ * before calling this method. If {@code mbs} is not null, then
+ * {@code mbsf.setMBeanServer(mbs)} will be called. If doing so
+ * produces an exception, this method throws the same exception without
+ * any other effect. If {@code mbs} is null, or if the call to
+ * {@code mbsf.setMBeanServer(mbs)} succeeds, then this method will
+ * return normally and {@code getSystemMBeanServer()} will then return
+ * {@code mbsf}.</p>
+ *
+ * <p>The result of {@link #getMBeanServer()} is unchanged by this method.</p>
+ *
+ * @param mbsf the new <code>MBeanServerForwarder</code>.
+ *
+ * @throws IllegalArgumentException if the call to {@link
+ * MBeanServerForwarder#setMBeanServer mbsf.setMBeanServer} fails
+ * with <code>IllegalArgumentException</code>, or if
+ * <code>mbsf</code> is null.
+ *
+ * @throws UnsupportedOperationException if
+ * {@link #supportsSystemMBeanServerForwarder} returns false.
+ *
+ * @see #getSystemMBeanServer()
+ */
+ public synchronized void setSystemMBeanServerForwarder(
+ MBeanServerForwarder mbsf) {
+ if (mbsf == null)
+ throw new IllegalArgumentException("Invalid null argument: mbsf");
+ mustSupportSystemMBSF();
+
+ if (systemMBeanServer != null)
+ mbsf.setMBeanServer(systemMBeanServer);
+ systemMBeanServer = mbsf;
+ }
+
+ /**
+ * <p>Returns true if this connector server supports a system chain of
+ * {@link MBeanServerForwarder}s. The default implementation of this
+ * method returns false. Connector servers that do support the system
+ * chain must override this method to return true.
+ *
+ * @return true if this connector server supports the system chain of
+ * forwarders.
+ */
+ public boolean supportsSystemMBeanServerForwarder() {
+ return false;
+ }
+
+ private void mustSupportSystemMBSF() {
+ if (!supportsSystemMBeanServerForwarder()) {
+ throw new UnsupportedOperationException(
+ "System MBeanServerForwarder not supported by this " +
+ "connector server");
+ }
+ }
+
+ /**
+ * <p>Install {@link MBeanServerForwarder}s in the system chain
+ * based on the attributes in the given {@code Map}. A connector
+ * server that {@linkplain #supportsSystemMBeanServerForwarder supports}
+ * a system chain of {@code MBeanServerForwarder}s can call this method
+ * to add forwarders to that chain based on the contents of {@code env}.
+ * In order:</p>
+ *
+ * <ul>
+ *
+ * <li>If {@link #EVENT_CLIENT_DELEGATE_FORWARDER} is absent, or is
+ * present with the value {@code "true"}, then a forwarder with the
+ * functionality of {@link EventClientDelegate#newForwarder} is inserted
+ * at the start of the system chain.</li>
+ *
+ * </ul>
+ *
+ * <p>For {@code EVENT_CLIENT_DELEGATE_FORWARDER}, if the
+ * attribute is absent from the {@code Map} and a system property
+ * of the same name is defined, then the value of the system
+ * property is used as if it were in the {@code Map}.
+ *
+ * <p>Attributes in {@code env} that are not listed above are ignored
+ * by this method.</p>
+ *
+ * @throws UnsupportedOperationException if {@link
+ * #supportsSystemMBeanServerForwarder} is false.
+ */
+ protected void installStandardForwarders(Map<String, ?> env) {
+ mustSupportSystemMBSF();
+
+ // Remember that forwarders must be added in reverse order!
+
+ boolean ecd = EnvHelp.computeBooleanFromString(
+ env, EVENT_CLIENT_DELEGATE_FORWARDER, false, true);
+
+ if (ecd) {
+ MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
+ setSystemMBeanServerForwarder(mbsf);
+ }
}
public String[] getConnectionIds() {
@@ -359,8 +645,8 @@ public abstract class JMXConnectorServer
ObjectName name) {
if (mbs == null || name == null)
throw new NullPointerException("Null MBeanServer or ObjectName");
- if (mbeanServer == null) {
- mbeanServer = mbs;
+ if (userMBeanServer == null) {
+ insertUserMBeanServer(mbs);
myName = name;
}
return name;
@@ -394,10 +680,53 @@ public abstract class JMXConnectorServer
myName = null;
}
- /**
- * The MBeanServer used by this server to execute a client request.
- */
- private MBeanServer mbeanServer = null;
+ /*
+ * Fields describing the chains of forwarders (MBeanServerForwarders).
+ * In the general case, the forwarders look something like this:
+ *
+ * systemMBeanServer userMBeanServer
+ * | |
+ * v v
+ * mbsf1 -> mbsf2 -> mbsf3 -> mbsf4 -> mbsf5 -> mbs
+ *
+ * Here, each mbsfi is an MBeanServerForwarder, and the arrows
+ * illustrate its getMBeanServer() method. The last MBeanServerForwarder
+ * can point to an MBeanServer that is not instanceof MBeanServerForwarder,
+ * here mbs.
+ *
+ * Initially, the chain can be empty if this JMXConnectorServer was
+ * constructed without an MBeanServer. In this case, both systemMBS
+ * and userMBS will be null. If there is initially an MBeanServer,
+ * then both systemMBS and userMBS will point to it.
+ *
+ * Whenever userMBS is changed, the system chain must be updated. If there
+ * are forwarders in the system chain (between systemMBS and userMBS in the
+ * picture above), then the last one must point to the old value of userMBS
+ * (possibly null). It must be updated to point to the new value. If there
+ * are no forwarders in the system chain, then systemMBS must be updated to
+ * the new value of userMBS. The invariant is that starting from systemMBS
+ * and repeatedly calling MBSF.getMBeanServer() you will end up at
+ * userMBS. The implication is that you will not see any MBeanServer
+ * object on the way that is not also an MBeanServerForwarder.
+ *
+ * The method insertUserMBeanServer contains the logic to change userMBS
+ * and adjust the system chain appropriately.
+ *
+ * If userMBS is null and this JMXConnectorServer is registered in an
+ * MBeanServer, then userMBS becomes that MBeanServer, and the system
+ * chain must be updated as just described.
+ *
+ * When systemMBS is updated, there is no effect on userMBS. The system
+ * chain may contain forwarders even though the user chain is empty
+ * (there is no MBeanServer). In that case an attempt to forward an
+ * incoming request through the chain will fall off the end and fail with a
+ * NullPointerException. Usually a connector server will refuse to start()
+ * if it is not attached to an MBS, so this situation should not arise.
+ */
+
+ private MBeanServer userMBeanServer;
+
+ private MBeanServer systemMBeanServer;
/**
* The name used to registered this server in an MBeanServer.
--- a/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java Thu Jul 31 15:31:13 2008 +0200
@@ -35,10 +35,8 @@ import java.util.HashMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
-import java.util.ServiceLoader;
import javax.management.MBeanServer;
-import javax.management.ObjectName;
/**
* <p>Factory to create JMX API connector servers. There
@@ -172,7 +170,8 @@ public class JMXConnectorServerFactory {
* loader MBean name. This class loader is used to deserialize objects in
* requests received from the client, possibly after consulting an
* MBean-specific class loader. The value associated with this
- * attribute is an instance of {@link ObjectName}.</p>
+ * attribute is an instance of {@link javax.management.ObjectName
+ * ObjectName}.</p>
*/
public static final String DEFAULT_CLASS_LOADER_NAME =
"jmx.remote.default.class.loader.name";
--- a/src/share/classes/javax/management/remote/JMXConnectorServerMBean.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/remote/JMXConnectorServerMBean.java Thu Jul 31 15:31:13 2008 +0200
@@ -105,23 +105,34 @@ public interface JMXConnectorServerMBean
public boolean isActive();
/**
- * <p>Adds an object that intercepts requests for the MBean server
+ * <p>Inserts an object that intercepts requests for the MBean server
* that arrive through this connector server. This object will be
* supplied as the <code>MBeanServer</code> for any new connection
* created by this connector server. Existing connections are
* unaffected.</p>
*
- * <p>If this connector server is already associated with an
+ * <p>This method can be called more than once with different
+ * {@link MBeanServerForwarder} objects. The result is a chain
+ * of forwarders. The last forwarder added is the first in the chain.
+ * In more detail:</p>
+ *
+ * <ul>
+ * <li><p>If this connector server is already associated with an
* <code>MBeanServer</code> object, then that object is given to
* {@link MBeanServerForwarder#setMBeanServer
* mbsf.setMBeanServer}. If doing so produces an exception, this
* method throws the same exception without any other effect.</p>
*
- * <p>If this connector is not already associated with an
+ * <li><p>If this connector is not already associated with an
* <code>MBeanServer</code> object, or if the
* <code>mbsf.setMBeanServer</code> call just mentioned succeeds,
* then <code>mbsf</code> becomes this connector server's
* <code>MBeanServer</code>.</p>
+ * </ul>
+ *
+ * <p>A connector server may support two chains of forwarders,
+ * a system chain and a user chain. See {@link
+ * JMXConnectorServer#setSystemMBeanServerForwarder} for details.</p>
*
* @param mbsf the new <code>MBeanServerForwarder</code>.
*
@@ -129,6 +140,8 @@ public interface JMXConnectorServerMBean
* MBeanServerForwarder#setMBeanServer mbsf.setMBeanServer} fails
* with <code>IllegalArgumentException</code>. This includes the
* case where <code>mbsf</code> is null.
+ *
+ * @see JMXConnectorServer#setSystemMBeanServerForwarder
*/
public void setMBeanServerForwarder(MBeanServerForwarder mbsf);
--- a/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java Thu Jul 31 15:31:13 2008 +0200
@@ -25,10 +25,12 @@
package javax.management.remote.rmi;
+import com.sun.jmx.mbeanserver.Util;
import static com.sun.jmx.mbeanserver.Util.cast;
import com.sun.jmx.remote.internal.ServerCommunicatorAdmin;
import com.sun.jmx.remote.internal.ServerNotifForwarder;
import com.sun.jmx.remote.security.JMXSubjectDomainCombiner;
+import com.sun.jmx.remote.security.NotificationAccessController;
import com.sun.jmx.remote.security.SubjectDelegator;
import com.sun.jmx.remote.util.ClassLoaderWithRepository;
import com.sun.jmx.remote.util.ClassLogger;
@@ -36,6 +38,7 @@ import com.sun.jmx.remote.util.OrderClas
import com.sun.jmx.remote.util.OrderClassLoaders;
import java.io.IOException;
+import java.lang.reflect.UndeclaredThrowableException;
import java.rmi.MarshalledObject;
import java.rmi.UnmarshalException;
import java.rmi.server.Unreferenced;
@@ -56,19 +59,24 @@ import javax.management.InstanceNotFound
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
+import javax.management.JMX;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
+import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.RuntimeOperationsException;
-import javax.management.loading.ClassLoaderRepository;
+import javax.management.event.EventClientDelegate;
+import javax.management.event.EventClientDelegateMBean;
+import javax.management.event.EventClientNotFoundException;
+import javax.management.event.FetchingEventForwarder;
import javax.management.remote.JMXServerErrorException;
import javax.management.remote.NotificationResult;
import javax.management.remote.TargetedNotification;
@@ -149,28 +157,16 @@ public class RMIConnectionImpl implement
new PrivilegedAction<ClassLoaderWithRepository>() {
public ClassLoaderWithRepository run() {
return new ClassLoaderWithRepository(
- getClassLoaderRepository(),
- dcl);
+ mbeanServer.getClassLoaderRepository(),
+ dcl);
}
});
-
serverCommunicatorAdmin = new
RMIServerCommunicatorAdmin(EnvHelp.getServerConnectionTimeout(env));
this.env = env;
}
- private synchronized ServerNotifForwarder getServerNotifFwd() {
- // Lazily created when first use. Mainly when
- // addNotificationListener is first called.
- if (serverNotifForwarder == null)
- serverNotifForwarder =
- new ServerNotifForwarder(mbeanServer,
- env,
- rmiServer.getNotifBuffer(),
- connectionId);
- return serverNotifForwarder;
- }
public String getConnectionId() throws IOException {
// We should call reqIncomming() here... shouldn't we?
@@ -181,6 +177,7 @@ public class RMIConnectionImpl implement
final boolean debug = logger.debugOn();
final String idstr = (debug?"["+this.toString()+"]":null);
+ final SubscriptionManager mgr;
synchronized (this) {
if (terminated) {
if (debug) logger.debug("close",idstr + " already terminated.");
@@ -195,10 +192,11 @@ public class RMIConnectionImpl implement
serverCommunicatorAdmin.terminate();
}
- if (serverNotifForwarder != null) {
- serverNotifForwarder.terminate();
- }
- }
+ mgr = subscriptionManager;
+ subscriptionManager = null;
+ }
+
+ if (mgr != null) mgr.terminate();
rmiServer.clientClosed(this);
@@ -955,8 +953,7 @@ public class RMIConnectionImpl implement
int i=0;
ClassLoader targetCl;
NotificationFilter[] filterValues =
- new NotificationFilter[names.length];
- Object params[];
+ new NotificationFilter[names.length];
Integer[] ids = new Integer[names.length];
final boolean debug=logger.debugOn();
@@ -991,8 +988,7 @@ public class RMIConnectionImpl implement
// remove all registered listeners
for (int j=0; j<i; j++) {
try {
- getServerNotifFwd().removeNotificationListener(names[j],
- ids[j]);
+ doRemoveListener(names[j],ids[j]);
} catch (Exception eee) {
// strange
}
@@ -1240,20 +1236,297 @@ public class RMIConnectionImpl implement
final long csn = clientSequenceNumber;
final int mn = maxNotifications;
final long t = timeout;
- PrivilegedAction<NotificationResult> action =
- new PrivilegedAction<NotificationResult>() {
- public NotificationResult run() {
- return getServerNotifFwd().fetchNotifs(csn, t, mn);
+
+ final PrivilegedExceptionAction<NotificationResult> action =
+ new PrivilegedExceptionAction<NotificationResult>() {
+ public NotificationResult run() throws IOException {
+ return doFetchNotifs(csn, t, mn);
}
};
- if (acc == null)
- return action.run();
- else
- return AccessController.doPrivileged(action, acc);
+ try {
+ if (acc == null)
+ return action.run();
+ else
+ return AccessController.doPrivileged(action, acc);
+ } catch (IOException x) {
+ throw x;
+ } catch (RuntimeException x) {
+ throw x;
+ } catch (Exception x) {
+ // should not happen
+ throw new UndeclaredThrowableException(x);
+ }
+
} finally {
serverCommunicatorAdmin.rspOutgoing();
}
}
+
+ /**
+ * This is an abstraction class that let us use the legacy
+ * ServerNotifForwarder and the new EventClientDelegateMBean
+ * indifferently.
+ **/
+ private static interface SubscriptionManager {
+ public void removeNotificationListener(ObjectName name, Integer id)
+ throws InstanceNotFoundException, ListenerNotFoundException, IOException;
+ public void removeNotificationListener(ObjectName name, Integer[] ids)
+ throws Exception;
+ public NotificationResult fetchNotifications(long csn, long timeout, int maxcount)
+ throws IOException;
+ public Integer addNotificationListener(ObjectName name, NotificationFilter filter)
+ throws InstanceNotFoundException, IOException;
+ public void terminate()
+ throws IOException;
+ }
+
+ /**
+ * A SubscriptionManager that uses a ServerNotifForwarder.
+ **/
+ private static class LegacySubscriptionManager implements SubscriptionManager {
+ private final ServerNotifForwarder forwarder;
+ LegacySubscriptionManager(ServerNotifForwarder forwarder) {
+ this.forwarder = forwarder;
+ }
+
+ public void removeNotificationListener(ObjectName name, Integer id)
+ throws InstanceNotFoundException, ListenerNotFoundException,
+ IOException {
+ forwarder.removeNotificationListener(name,id);
+ }
+
+ public void removeNotificationListener(ObjectName name, Integer[] ids)
+ throws Exception {
+ forwarder.removeNotificationListener(name,ids);
+ }
+
+ public NotificationResult fetchNotifications(long csn, long timeout, int maxcount) {
+ return forwarder.fetchNotifs(csn,timeout,maxcount);
+ }
+
+ public Integer addNotificationListener(ObjectName name,
+ NotificationFilter filter)
+ throws InstanceNotFoundException, IOException {
+ return forwarder.addNotificationListener(name,filter);
+ }
+
+ public void terminate() {
+ forwarder.terminate();
+ }
+ }
+
+ /**
+ * A SubscriptionManager that uses an EventClientDelegateMBean.
+ **/
+ private static class EventSubscriptionManager
+ implements SubscriptionManager {
+ private final MBeanServer mbeanServer;
+ private final EventClientDelegateMBean delegate;
+ private final NotificationAccessController notifAC;
+ private final boolean checkNotificationEmission;
+ private final String clientId;
+ private final String connectionId;
+
+ EventSubscriptionManager(
+ MBeanServer mbeanServer,
+ EventClientDelegateMBean delegate,
+ Map<String, ?> env,
+ String clientId,
+ String connectionId) {
+ this.mbeanServer = mbeanServer;
+ this.delegate = delegate;
+ this.notifAC = EnvHelp.getNotificationAccessController(env);
+ this.checkNotificationEmission =
+ EnvHelp.computeBooleanFromString(
+ env, "jmx.remote.x.check.notification.emission", false);
+ this.clientId = clientId;
+ this.connectionId = connectionId;
+ }
+
+ @SuppressWarnings("serial") // no serialVersionUID
+ private class AccessControlFilter implements NotificationFilter {
+ private final NotificationFilter wrapped;
+ private final ObjectName name;
+
+ AccessControlFilter(ObjectName name, NotificationFilter wrapped) {
+ this.name = name;
+ this.wrapped = wrapped;
+ }
+
+ public boolean isNotificationEnabled(Notification notification) {
+ try {
+ if (checkNotificationEmission) {
+ ServerNotifForwarder.checkMBeanPermission(
+ mbeanServer, name, "addNotificationListener");
+ }
+ notifAC.fetchNotification(
+ connectionId, name, notification, getSubject());
+ return (wrapped == null) ? true :
+ wrapped.isNotificationEnabled(notification);
+ } catch (InstanceNotFoundException e) {
+ return false;
+ } catch (SecurityException e) {
+ return false;
+ }
+ }
+
+ }
+
+ public Integer addNotificationListener(
+ ObjectName name, NotificationFilter filter)
+ throws InstanceNotFoundException, IOException {
+ if (notifAC != null) {
+ notifAC.addNotificationListener(connectionId, name, getSubject());
+ filter = new AccessControlFilter(name, filter);
+ }
+ try {
+ return delegate.addListener(clientId,name,filter);
+ } catch (EventClientNotFoundException x) {
+ throw new IOException("Unknown clientId: "+clientId,x);
+ }
+ }
+
+ public void removeNotificationListener(ObjectName name, Integer id)
+ throws InstanceNotFoundException, ListenerNotFoundException,
+ IOException {
+ if (notifAC != null)
+ notifAC.removeNotificationListener(connectionId, name, getSubject());
+ try {
+ delegate.removeListenerOrSubscriber(clientId,id);
+ } catch (EventClientNotFoundException x) {
+ throw new IOException("Unknown clientId: "+clientId,x);
+ }
+ }
+
+ public void removeNotificationListener(ObjectName name, Integer[] ids)
+ throws InstanceNotFoundException, ListenerNotFoundException,
+ IOException {
+ if (notifAC != null)
+ notifAC.removeNotificationListener(connectionId, name, getSubject());
+ try {
+ for (Integer id : ids)
+ delegate.removeListenerOrSubscriber(clientId,id);
+ } catch (EventClientNotFoundException x) {
+ throw new IOException("Unknown clientId: "+clientId,x);
+ }
+ }
+
+ public NotificationResult fetchNotifications(long csn, long timeout,
+ int maxcount)
+ throws IOException {
+ try {
+ // For some reason the delegate doesn't accept a negative
+ // sequence number. However legacy clients will always call
+ // fetchNotifications with a negative sequence number, when
+ // they call it for the first time.
+ // In that case, we will use 0 instead.
+ //
+ return delegate.fetchNotifications(
+ clientId, Math.max(csn, 0), maxcount, timeout);
+ } catch (EventClientNotFoundException x) {
+ throw new IOException("Unknown clientId: "+clientId,x);
+ }
+ }
+
+ public void terminate()
+ throws IOException {
+ try {
+ delegate.removeClient(clientId);
+ } catch (EventClientNotFoundException x) {
+ throw new IOException("Unknown clientId: "+clientId,x);
+ }
+ }
+
+ private static Subject getSubject() {
+ return Subject.getSubject(AccessController.getContext());
+ }
+ }
+
+ /**
+ * Creates a SubscriptionManager that uses either the legacy notifications
+ * mechanism (ServerNotifForwarder) or the new event service
+ * (EventClientDelegateMBean) depending on which option was passed in
+ * the connector's map.
+ **/
+ private SubscriptionManager createSubscriptionManager()
+ throws IOException {
+ if (EnvHelp.delegateToEventService(env) &&
+ mbeanServer.isRegistered(EventClientDelegate.OBJECT_NAME)) {
+ final EventClientDelegateMBean mbean =
+ JMX.newMBeanProxy(mbeanServer,
+ EventClientDelegate.OBJECT_NAME,
+ EventClientDelegateMBean.class);
+ String clientId;
+ try {
+ clientId =
+ mbean.addClient(
+ FetchingEventForwarder.class.getName(),
+ new Object[] {EnvHelp.getNotifBufferSize(env)},
+ new String[] {int.class.getName()});
+ } catch (Exception e) {
+ if (e instanceof IOException)
+ throw (IOException) e;
+ else
+ throw new IOException(e);
+ }
+
+ // we're going to call remove client...
+ try {
+ mbean.lease(clientId, Long.MAX_VALUE);
+ } catch (EventClientNotFoundException x) {
+ throw new IOException("Unknown clientId: "+clientId,x);
+ }
+ return new EventSubscriptionManager(mbeanServer, mbean, env,
+ clientId, connectionId);
+ } else {
+ final ServerNotifForwarder serverNotifForwarder =
+ new ServerNotifForwarder(mbeanServer,
+ env,
+ rmiServer.getNotifBuffer(),
+ connectionId);
+ return new LegacySubscriptionManager(serverNotifForwarder);
+ }
+ }
+
+ /**
+ * Lazy creation of a SubscriptionManager.
+ **/
+ private synchronized SubscriptionManager getSubscriptionManager()
+ throws IOException {
+ // Lazily created when first use. Mainly when
+ // addNotificationListener is first called.
+
+ if (subscriptionManager == null) {
+ subscriptionManager = createSubscriptionManager();
+ }
+ return subscriptionManager;
+ }
+
+ // calls SubscriptionManager.
+ private void doRemoveListener(ObjectName name, Integer id)
+ throws InstanceNotFoundException, ListenerNotFoundException,
+ IOException {
+ getSubscriptionManager().removeNotificationListener(name,id);
+ }
+
+ // calls SubscriptionManager.
+ private void doRemoveListener(ObjectName name, Integer[] ids)
+ throws Exception {
+ getSubscriptionManager().removeNotificationListener(name,ids);
+ }
+
+ // calls SubscriptionManager.
+ private NotificationResult doFetchNotifs(long csn, long timeout, int maxcount)
+ throws IOException {
+ return getSubscriptionManager().fetchNotifications(csn, timeout, maxcount);
+ }
+
+ // calls SubscriptionManager.
+ private Integer doAddListener(ObjectName name, NotificationFilter filter)
+ throws InstanceNotFoundException, IOException {
+ return getSubscriptionManager().addNotificationListener(name,filter);
+ }
+
/**
* <p>Returns a string representation of this object. In general,
@@ -1312,16 +1585,6 @@ public class RMIConnectionImpl implement
//------------------------------------------------------------------------
// private methods
//------------------------------------------------------------------------
-
- private ClassLoaderRepository getClassLoaderRepository() {
- return
- AccessController.doPrivileged(
- new PrivilegedAction<ClassLoaderRepository>() {
- public ClassLoaderRepository run() {
- return mbeanServer.getClassLoaderRepository();
- }
- });
- }
private ClassLoader getClassLoader(final ObjectName name)
throws InstanceNotFoundException {
@@ -1482,9 +1745,8 @@ public class RMIConnectionImpl implement
return null;
case ADD_NOTIFICATION_LISTENERS:
- return getServerNotifFwd().addNotificationListener(
- (ObjectName)params[0],
- (NotificationFilter)params[1]);
+ return doAddListener((ObjectName)params[0],
+ (NotificationFilter)params[1]);
case ADD_NOTIFICATION_LISTENER_OBJECTNAME:
mbeanServer.addNotificationListener((ObjectName)params[0],
@@ -1494,9 +1756,7 @@ public class RMIConnectionImpl implement
return null;
case REMOVE_NOTIFICATION_LISTENER:
- getServerNotifFwd().removeNotificationListener(
- (ObjectName)params[0],
- (Integer[])params[1]);
+ doRemoveListener((ObjectName)params[0],(Integer[])params[1]);
return null;
case REMOVE_NOTIFICATION_LISTENER_OBJECTNAME:
@@ -1709,23 +1969,21 @@ public class RMIConnectionImpl implement
private final static int
REMOVE_NOTIFICATION_LISTENER = 19;
private final static int
- REMOVE_NOTIFICATION_LISTENER_FILTER_HANDBACK = 20;
- private final static int
- REMOVE_NOTIFICATION_LISTENER_OBJECTNAME = 21;
- private final static int
- REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK = 22;
- private final static int
- SET_ATTRIBUTE = 23;
- private final static int
- SET_ATTRIBUTES = 24;
- private final static int
- UNREGISTER_MBEAN = 25;
+ REMOVE_NOTIFICATION_LISTENER_OBJECTNAME = 20;
+ private final static int
+ REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK = 21;
+ private final static int
+ SET_ATTRIBUTE = 22;
+ private final static int
+ SET_ATTRIBUTES = 23;
+ private final static int
+ UNREGISTER_MBEAN = 24;
// SERVER NOTIFICATION
//--------------------
- private ServerNotifForwarder serverNotifForwarder;
- private Map env;
+ private SubscriptionManager subscriptionManager;
+ private Map<String, ?> env;
// TRACES & DEBUG
//---------------
--- a/src/share/classes/javax/management/remote/rmi/RMIConnector.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/remote/rmi/RMIConnector.java Thu Jul 31 15:31:13 2008 +0200
@@ -25,6 +25,9 @@
package javax.management.remote.rmi;
+import com.sun.jmx.event.DaemonThreadFactory;
+import com.sun.jmx.event.EventConnection;
+import com.sun.jmx.mbeanserver.PerThreadGroupPool;
import com.sun.jmx.remote.internal.ClientCommunicatorAdmin;
import com.sun.jmx.remote.internal.ClientListenerInfo;
import com.sun.jmx.remote.internal.ClientNotifForwarder;
@@ -68,6 +71,12 @@ import java.util.Properties;
import java.util.Properties;
import java.util.Set;
import java.util.WeakHashMap;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
@@ -75,6 +84,7 @@ import javax.management.InstanceNotFound
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
+import javax.management.JMX;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
@@ -92,6 +102,8 @@ import javax.management.ObjectName;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
+import javax.management.event.EventClient;
+import javax.management.event.EventClientDelegateMBean;
import javax.management.remote.JMXConnectionNotification;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
@@ -280,8 +292,8 @@ public class RMIConnector implements JMX
// client-side environment property is set to "true".
//
boolean checkStub = EnvHelp.computeBooleanFromString(
- usemap,
- "jmx.remote.x.check.stub");
+ usemap,
+ "jmx.remote.x.check.stub",false);
if (checkStub) checkStub(stub, rmiServerImplStubClass);
// Connect IIOP Stub if needed.
@@ -317,6 +329,8 @@ public class RMIConnector implements JMX
// reconnecting, to identify the "old" connection.
//
connectionId = getConnectionId();
+
+ eventServiceEnabled = EnvHelp.eventServiceEnabled(env);
Notification connectedNotif =
new JMXConnectionNotification(JMXConnectionNotification.OPENED,
@@ -326,6 +340,8 @@ public class RMIConnector implements JMX
"Successful connection",
null);
sendNotification(connectedNotif);
+
+ // whether or not event service
if (tracing) logger.trace("connect",idstr + " done...");
} catch (IOException e) {
@@ -378,13 +394,42 @@ public class RMIConnector implements JMX
throw new IOException("Not connected");
}
- MBeanServerConnection mbsc = rmbscMap.get(delegationSubject);
- if (mbsc != null)
- return mbsc;
-
- mbsc = new RemoteMBeanServerConnection(delegationSubject);
- rmbscMap.put(delegationSubject, mbsc);
- return mbsc;
+ MBeanServerConnection rmbsc = rmbscMap.get(delegationSubject);
+ if (rmbsc != null) {
+ return rmbsc;
+ }
+
+ rmbsc = new RemoteMBeanServerConnection(delegationSubject);
+ if (eventServiceEnabled) {
+ EventClientDelegateMBean ecd = JMX.newMBeanProxy(
+ rmbsc, EventClientDelegateMBean.OBJECT_NAME,
+ EventClientDelegateMBean.class);
+ EventClient ec = new EventClient(ecd, null, defaultExecutor(), null,
+ EventClient.DEFAULT_LEASE_TIMEOUT);
+
+ rmbsc = EventConnection.Factory.make(rmbsc, ec);
+ ec.addEventClientListener(
+ lostNotifListener, null, null);
+ }
+ rmbscMap.put(delegationSubject, rmbsc);
+ return rmbsc;
+ }
+
+ private static Executor defaultExecutor() {
+ PerThreadGroupPool.Create<ThreadPoolExecutor> create =
+ new PerThreadGroupPool.Create<ThreadPoolExecutor>() {
+ public ThreadPoolExecutor createThreadPool(ThreadGroup group) {
+ ThreadFactory daemonThreadFactory = new DaemonThreadFactory(
+ "RMIConnector listener dispatch %d");
+ ThreadPoolExecutor exec = new ThreadPoolExecutor(
+ 1, 10, 1, TimeUnit.SECONDS,
+ new LinkedBlockingDeque<Runnable>(),
+ daemonThreadFactory);
+ exec.allowCoreThreadTimeOut(true);
+ return exec;
+ }
+ };
+ return listenerDispatchThreadPool.getThreadPoolExecutor(create);
}
public void
@@ -464,6 +509,17 @@ public class RMIConnector implements JMX
if (communicatorAdmin != null) {
communicatorAdmin.terminate();
+ }
+
+ // close all EventClient
+ for (MBeanServerConnection rmbsc : rmbscMap.values()) {
+ if (rmbsc instanceof EventConnection) {
+ try {
+ ((EventConnection)rmbsc).getEventClient().close();
+ } catch (Exception e) {
+ // OK
+ }
+ }
}
if (rmiNotifClient != null) {
@@ -592,17 +648,18 @@ public class RMIConnector implements JMX
}
if (debug) logger.debug("addListenersWithSubjects","registered "
- + listenerIDs.length + " listener(s)");
+ + ((listenerIDs==null)?0:listenerIDs.length)
+ + " listener(s)");
return listenerIDs;
}
//--------------------------------------------------------------------
// Implementation of MBeanServerConnection
//--------------------------------------------------------------------
- private class RemoteMBeanServerConnection
- implements MBeanServerConnection {
-
+ private class RemoteMBeanServerConnection implements MBeanServerConnection {
private Subject delegationSubject;
+
+ public EventClient eventClient = null;
public RemoteMBeanServerConnection() {
this(null);
@@ -1205,6 +1262,7 @@ public class RMIConnector implements JMX
IOException {
final boolean debug = logger.debugOn();
+
if (debug)
logger.debug("addNotificationListener" +
"(ObjectName,NotificationListener,"+
@@ -1226,8 +1284,9 @@ public class RMIConnector implements JMX
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException,
- ListenerNotFoundException,
- IOException {
+ ListenerNotFoundException,
+ IOException {
+
final boolean debug = logger.debugOn();
if (debug) logger.debug("removeNotificationListener"+
@@ -1804,6 +1863,26 @@ public class RMIConnector implements JMX
terminated = false;
connectionBroadcaster = new NotificationBroadcasterSupport();
+
+ lostNotifListener =
+ new NotificationListener() {
+ public void handleNotification(Notification n, Object hb) {
+ if (n != null && EventClient.NOTIFS_LOST.equals(n.getType())) {
+ Long lost = (Long)n.getUserData();
+ final String msg =
+ "May have lost up to " + lost +
+ " notification" + (lost.longValue() == 1 ? "" : "s");
+ sendNotification(new JMXConnectionNotification(
+ JMXConnectionNotification.NOTIFS_LOST,
+ RMIConnector.this,
+ connectionId,
+ clientNotifCounter++,
+ msg,
+ lost));
+
+ }
+ }
+ };
}
//--------------------------------------------------------------------
@@ -2528,6 +2607,11 @@ public class RMIConnector implements JMX
private transient ClientCommunicatorAdmin communicatorAdmin;
+ private boolean eventServiceEnabled;
+// private transient EventRelay eventRelay;
+
+ private transient NotificationListener lostNotifListener;
+
/**
* A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to
* connect unconnected stubs.
@@ -2546,4 +2630,7 @@ public class RMIConnector implements JMX
private static String strings(final String[] strs) {
return objects(strs);
}
+
+ private static final PerThreadGroupPool<ThreadPoolExecutor> listenerDispatchThreadPool =
+ PerThreadGroupPool.make();
}
--- a/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java Thu Jul 31 14:20:11 2008 +0200
+++ b/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java Thu Jul 31 15:31:13 2008 +0200
@@ -230,6 +230,8 @@ public class RMIConnectorServer extends
this.address = url;
this.rmiServerImpl = rmiServerImpl;
+
+ installStandardForwarders(this.attributes);
}
/**
@@ -380,8 +382,8 @@ public class RMIConnectorServer extends
try {
if (tracing) logger.trace("start", "setting default class loader");
- defaultClassLoader =
- EnvHelp.resolveServerClassLoader(attributes, getMBeanServer());
+ defaultClassLoader = EnvHelp.resolveServerClassLoader(
+ attributes, getSystemMBeanServer());
} catch (InstanceNotFoundException infc) {
IllegalArgumentException x = new
IllegalArgumentException("ClassLoader not found: "+infc);
@@ -396,7 +398,7 @@ public class RMIConnectorServer extends
else
rmiServer = newServer();
- rmiServer.setMBeanServer(getMBeanServer());
+ rmiServer.setMBeanServer(getSystemMBeanServer());
rmiServer.setDefaultClassLoader(defaultClassLoader);
rmiServer.setRMIConnectorServer(this);
rmiServer.export();
@@ -413,7 +415,7 @@ public class RMIConnectorServer extends
final boolean rebind = EnvHelp.computeBooleanFromString(
attributes,
- JNDI_REBIND_ATTRIBUTE);
+ JNDI_REBIND_ATTRIBUTE,false);
if (tracing)
logger.trace("start", JNDI_REBIND_ATTRIBUTE + "=" + rebind);
@@ -590,11 +592,39 @@ public class RMIConnectorServer extends
return Collections.unmodifiableMap(map);
}
- public synchronized
- void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
+ @Override
+ public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
+ MBeanServer oldSMBS = getSystemMBeanServer();
super.setMBeanServerForwarder(mbsf);
+ if (oldSMBS != getSystemMBeanServer())
+ updateMBeanServer();
+ // If the system chain of MBeanServerForwarders is not empty, then
+ // there is no need to call rmiServerImpl.setMBeanServer, because
+ // it is pointing to the head of the system chain and that has not
+ // changed. (The *end* of the system chain will have been changed
+ // to point to mbsf.)
+ }
+
+ private void updateMBeanServer() {
if (rmiServerImpl != null)
- rmiServerImpl.setMBeanServer(getMBeanServer());
+ rmiServerImpl.setMBeanServer(getSystemMBeanServer());
+ }
+
+ @Override
+ public synchronized void setSystemMBeanServerForwarder(
+ MBeanServerForwarder mbsf) {
+ super.setSystemMBeanServerForwarder(mbsf);
+ updateMBeanServer();
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return true, since this connector server does support a system chain
+ * of forwarders.
+ */
+ @Override
+ public boolean supportsSystemMBeanServerForwarder() {
+ return true;
}
/* We repeat the definitions of connection{Opened,Closed,Failed}
--- a/test/javax/management/mxbean/GenericArrayTypeTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/mxbean/GenericArrayTypeTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -32,17 +32,19 @@
*/
import java.lang.management.ManagementFactory;
-import java.lang.management.MonitorInfo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.management.Attribute;
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
+import javax.management.StandardMBean;
+import javax.management.openmbean.CompositeData;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXConnectorServer;
@@ -50,6 +52,58 @@ import javax.management.remote.JMXServic
import javax.management.remote.JMXServiceURL;
public class GenericArrayTypeTest {
+ // A version of java.lang.management.MonitorInfo so we can run this test
+ // on JDK 5, where that class didn't exist.
+ public static class MonitorInfo {
+ private final String className;
+ private final int identityHashCode;
+ private final int lockedStackDepth;
+ private final StackTraceElement lockedStackFrame;
+
+ public MonitorInfo(
+ String className, int identityHashCode,
+ int lockedStackDepth, StackTraceElement lockedStackFrame) {
+ this.className = className;
+ this.identityHashCode = identityHashCode;
+ this.lockedStackDepth = lockedStackDepth;
+ this.lockedStackFrame = lockedStackFrame;
+ }
+
+ public static MonitorInfo from(CompositeData cd) {
+ try {
+ CompositeData stecd = (CompositeData) cd.get("lockedStackFrame");
+ StackTraceElement ste = new StackTraceElement(
+ (String) stecd.get("className"),
+ (String) stecd.get("methodName"),
+ (String) stecd.get("fileName"),
+ (Integer) stecd.get("lineNumber"));
+ return new MonitorInfo(
+ (String) cd.get("className"),
+ (Integer) cd.get("identityHashCode"),
+ (Integer) cd.get("lockedStackDepth"),
+ ste);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public int getIdentityHashCode() {
+ return identityHashCode;
+ }
+
+ public int getLockedStackDepth() {
+ return lockedStackDepth;
+ }
+
+ public StackTraceElement getLockedStackFrame() {
+ return lockedStackFrame;
+ }
+ }
+
public interface TestMXBean {
--- a/test/javax/management/mxbean/LeakTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/mxbean/LeakTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -25,7 +25,7 @@
* @bug 6482247
* @summary Test that creating MXBeans does not introduce memory leaks.
* @author Eamonn McManus
- * @run build LeakTest
+ * @run build LeakTest RandomMXBeanTest
* @run main LeakTest
*/
--- a/test/javax/management/mxbean/MBeanOperationInfoTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/mxbean/MBeanOperationInfoTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -86,7 +86,8 @@ public class MBeanOperationInfoTest {
if (error > 0) {
System.out.println("\nTEST FAILED");
throw new Exception("TEST FAILED: " + error + " wrong return types");
- } else if (tested != returnTypes.length) {
+ } else if (tested != returnTypes.length &&
+ !System.getProperty("java.specification.version").equals("1.5")) {
System.out.println("\nTEST FAILED");
throw new Exception("TEST FAILED: " + tested + " cases tested, " +
returnTypes.length + " expected");
--- a/test/javax/management/mxbean/MXBeanTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/mxbean/MXBeanTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -149,7 +149,7 @@ public class MXBeanTest {
if (mbai.getName().equals("Ints")
&& mbai.isReadable() && !mbai.isWritable()
&& mbai.getDescriptor().getFieldValue("openType")
- .equals(new ArrayType(SimpleType.INTEGER, true))
+ .equals(new ArrayType<int[]>(SimpleType.INTEGER, true))
&& attrs[0].getType().equals("[I"))
success("MBeanAttributeInfo");
else
--- a/test/javax/management/mxbean/ThreadMXBeanTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/mxbean/ThreadMXBeanTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -46,7 +46,8 @@ public class ThreadMXBeanTest {
long[] ids1 = proxy.getAllThreadIds();
// Add some random ids to the list so we'll get back null ThreadInfo
- long[] ids2 = Arrays.copyOf(ids1, ids1.length + 10);
+ long[] ids2 = new long[ids1.length + 10];
+ System.arraycopy(ids1, 0, ids2, 0, ids1.length);
Random r = new Random();
for (int i = ids1.length; i < ids2.length; i++)
ids2[i] = Math.abs(r.nextLong());
--- a/test/javax/management/mxbean/TigerMXBean.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/mxbean/TigerMXBean.java Thu Jul 31 15:31:13 2008 +0200
@@ -83,20 +83,20 @@ public interface TigerMXBean {
Tuiseal opEnum(Tuiseal x, Tuiseal y);
List<String> StringList = Arrays.asList(new String[] {"a", "b", "x"});
- ArrayType StringListType =
+ ArrayType<?> StringListType =
MerlinMXBean.ArrayTypeMaker.make(1, SimpleType.STRING);
List<String> getStringList();
void setStringList(List<String> x);
List<String> opStringList(List<String> x, List<String> y);
- Set<String> StringSet = new HashSet(StringList);
- ArrayType StringSetType = StringListType;
+ Set<String> StringSet = new HashSet<String>(StringList);
+ ArrayType<?> StringSetType = StringListType;
Set<String> getStringSet();
void setStringSet(Set<String> x);
Set<String> opStringSet(Set<String> x, Set<String> y);
- SortedSet<String> SortedStringSet = new TreeSet(StringList);
- ArrayType SortedStringSetType = StringListType;
+ SortedSet<String> SortedStringSet = new TreeSet<String>(StringList);
+ ArrayType<?> SortedStringSetType = StringListType;
SortedSet<String> getSortedStringSet();
void setSortedStringSet(SortedSet<String> x);
SortedSet<String> opSortedStringSet(SortedSet<String> x,
@@ -119,7 +119,7 @@ public interface TigerMXBean {
Map<String,List<String>> y);
SortedMap<String,String> XSortedMap =
- new TreeMap(Collections.singletonMap("foo", "bar"));
+ new TreeMap<String,String>(Collections.singletonMap("foo", "bar"));
String XSortedMapTypeName =
"java.util.SortedMap<java.lang.String, java.lang.String>";
CompositeType XSortedMapRowType = MerlinMXBean.CompositeTypeMaker.make(
@@ -137,8 +137,8 @@ public interface TigerMXBean {
// For bug 6319960, try constructing Set and Map with non-Comparable
- Set<Point> PointSet = new HashSet(Collections.singleton(Point));
- ArrayType PointSetType =
+ Set<Point> PointSet = new HashSet<Point>(Collections.singleton(Point));
+ ArrayType<?> PointSetType =
MerlinMXBean.ArrayTypeMaker.make(1, PointType);
Set<Point> getPointSet();
void setPointSet(Set<Point> x);
--- a/test/javax/management/query/QueryNotifFilterTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/query/QueryNotifFilterTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -98,7 +98,7 @@ public class QueryNotifFilterTest {
this.queryString = queryString;
}
abstract boolean apply(MBeanServer mbs, ObjectName name) throws Exception;
- @Override
+ //@Override - doesn't override in JDK5
public boolean apply(ObjectName name) {
try {
return apply(getMBeanServer(), name);
--- a/test/javax/management/remote/mandatory/connection/CloseServerTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/connection/CloseServerTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -31,11 +31,16 @@
* @run main CloseServerTest
*/
+import com.sun.jmx.remote.util.EnvHelp;
import java.net.MalformedURLException;
-import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
import javax.management.*;
import javax.management.remote.*;
+import javax.management.remote.rmi.RMIConnectorServer;
public class CloseServerTest {
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
@@ -131,40 +136,54 @@ public class CloseServerTest {
server.stop();
- // with a client listener, but close the server first
- System.out.println(">>> Open, start a server, create a client, add a listener, close the server then the client.");
- server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
- server.start();
+ List<Map<String, String>> envs = Arrays.asList(
+ Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"),
+ Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "true"));
- addr = server.getAddress();
- client = JMXConnectorFactory.newJMXConnector(addr, null);
- client.connect(null);
+ for (Map<String, String> env : envs) {
+ System.out.println(
+ ">>>>>>>> " + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE +
+ " = " + env.get(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE));
- mserver = client.getMBeanServerConnection();
- mserver.addNotificationListener(delegateName, dummyListener, null, null);
+ // with a client listener, but close the server first
+ System.out.println(">>> Open, start a server, create a client, " +
+ "add a listener, close the server then the client.");
+ server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
+ server.start();
- server.stop();
+ addr = server.getAddress();
+ client = JMXConnectorFactory.newJMXConnector(addr, null);
+ client.connect(null);
- try {
+ mserver = client.getMBeanServerConnection();
+ mserver.addNotificationListener(delegateName, dummyListener, null, null);
+
+ server.stop();
+
+ try {
+ client.close();
+ } catch (Exception e) {
+ // ok, it is because the server has been closed.
+ }
+
+ // with a client listener, but close the client first
+ System.out.println(">>> Open, start a server, create a client, " +
+ "add a listener, close the client then the server.");
+ server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
+ server.start();
+
+ addr = server.getAddress();
+ client = JMXConnectorFactory.newJMXConnector(addr, null);
+ client.connect(null);
+
+ mserver = client.getMBeanServerConnection();
+ mserver.addNotificationListener(delegateName, dummyListener, null, null);
+
client.close();
- } catch (Exception e) {
- // ok, it is because the server has been closed.
+ server.stop();
}
-
- // with a client listener, but close the client first
- System.out.println(">>> Open, start a server, create a client, add a listener, close the client then the server.");
- server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
- server.start();
-
- addr = server.getAddress();
- client = JMXConnectorFactory.newJMXConnector(addr, null);
- client.connect(null);
-
- mserver = client.getMBeanServerConnection();
- mserver.addNotificationListener(delegateName, dummyListener, null, null);
-
- client.close();
- server.stop();
} catch (MalformedURLException e) {
System.out.println(">>> Skipping unsupported URL " + u);
return true;
--- a/test/javax/management/remote/mandatory/connection/DeadLockTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/connection/DeadLockTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -37,6 +37,7 @@ import java.util.HashMap;
import javax.management.*;
import javax.management.remote.*;
+import javax.management.remote.rmi.RMIConnectorServer;
public class DeadLockTest {
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
@@ -71,6 +72,9 @@ public class DeadLockTest {
// disable the client ping
env.put("jmx.remote.x.client.connection.check.period", "0");
+
+ // ensure we are not internally using the Event Service on the server
+ env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
try {
u = new JMXServiceURL(proto, null, 0);
--- a/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -50,6 +50,8 @@ import javax.management.remote.JMXConnec
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import com.sun.jmx.remote.util.EnvHelp;
+import java.util.Collections;
+import javax.management.remote.rmi.RMIConnectorServer;
public class IdleTimeoutTest {
public static void main(String[] args) throws Exception {
@@ -88,8 +90,13 @@ public class IdleTimeoutTest {
private static long getIdleTimeout(MBeanServer mbs, JMXServiceURL url)
throws Exception {
+ // If the connector server is using the Event Service, then connections
+ // never time out. This is by design.
+ Map<String, String> env =
+ Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
JMXConnectorServer server =
- JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
server.start();
try {
url = server.getAddress();
@@ -164,6 +171,7 @@ public class IdleTimeoutTest {
Map idleMap = new HashMap();
idleMap.put(EnvHelp.SERVER_CONNECTION_TIMEOUT, new Long(timeout));
+ idleMap.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
JMXConnectorServer server =
JMXConnectorServerFactory.newJMXConnectorServer(url,idleMap,mbs);
--- a/test/javax/management/remote/mandatory/connection/RMIExitTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/connection/RMIExitTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -35,6 +35,8 @@ import java.net.MalformedURLException;
import java.net.MalformedURLException;
import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
import javax.management.MBeanServerFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
@@ -47,12 +49,14 @@ import javax.management.remote.JMXConnec
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.rmi.RMIConnectorServer;
/**
* VM shutdown hook. Test that the hook is called less than 5 secs
* after expected exit.
*/
class TimeChecker extends Thread {
+ @Override
public void run() {
System.out.println("shutdown hook called");
long elapsedTime =
@@ -81,12 +85,15 @@ public class RMIExitTest {
public static void main(String[] args) {
System.out.println("Start test");
Runtime.getRuntime().addShutdownHook(new TimeChecker());
- test();
+ test(false);
+ test(true);
exitStartTime = System.currentTimeMillis();
System.out.println("End test");
}
- private static void test() {
+ private static void test(boolean eventService) {
+ System.out.println(
+ "---testing with" + (eventService ? "" : "out") + " Event Service");
try {
JMXServiceURL u = new JMXServiceURL("rmi", null, 0);
JMXConnectorServer server;
@@ -105,8 +112,11 @@ public class RMIExitTest {
}
};
+ Map<String, String> env = Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
+ Boolean.toString(eventService));
server = JMXConnectorServerFactory.newJMXConnectorServer(u,
- null,
+ env,
mbs);
server.start();
--- a/test/javax/management/remote/mandatory/connection/ReconnectTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/connection/ReconnectTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -33,10 +33,10 @@
import java.util.*;
import java.net.MalformedURLException;
-import java.io.IOException;
import javax.management.*;
import javax.management.remote.*;
+import javax.management.remote.rmi.RMIConnectorServer;
public class ReconnectTest {
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
@@ -48,6 +48,7 @@ public class ReconnectTest {
String timeout = "1000";
env.put("jmx.remote.x.server.connection.timeout", timeout);
env.put("jmx.remote.x.client.connection.check.period", timeout);
+ env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
}
public static void main(String[] args) throws Exception {
--- a/test/javax/management/remote/mandatory/loading/MissingClassTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/loading/MissingClassTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -44,13 +44,33 @@
We also test objects that are of known class but not serializable.
The test cases are similar.
*/
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import javax.management.*;
-import javax.management.loading.*;
-import javax.management.remote.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectOutputStream;
+import java.net.MalformedURLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerFactory;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnectionNotification;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
+import org.omg.CORBA.MARSHAL;
public class MissingClassTest {
private static final int NNOTIFS = 50;
@@ -84,7 +104,6 @@ public class MissingClassTest {
serverLoader.loadClass("$ClientUnknown$").newInstance();
final String[] protos = {"rmi", /*"iiop",*/ "jmxmp"};
- // iiop commented out until bug 4935098 is fixed
boolean ok = true;
for (int i = 0; i < protos.length; i++) {
try {
@@ -105,7 +124,16 @@ public class MissingClassTest {
}
private static boolean test(String proto) throws Exception {
- System.out.println("Testing for proto " + proto);
+ boolean ok = true;
+ for (boolean eventService : new boolean[] {false, true})
+ ok &= test(proto, eventService);
+ return ok;
+ }
+
+ private static boolean test(String proto, boolean eventService)
+ throws Exception {
+ System.out.println("Testing for proto " + proto + " with" +
+ (eventService ? "" : "out") + " Event Service");
boolean ok = true;
@@ -117,6 +145,8 @@ public class MissingClassTest {
Map serverMap = new HashMap();
serverMap.put(JMXConnectorServerFactory.DEFAULT_CLASS_LOADER,
serverLoader);
+ serverMap.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
+ Boolean.toString(eventService));
// make sure no auto-close at server side
serverMap.put("jmx.remote.x.server.connection.timeout", "888888888");
@@ -155,6 +185,8 @@ public class MissingClassTest {
ok = false;
} catch (IOException e) {
Throwable cause = e.getCause();
+ if (cause instanceof MARSHAL) // see CR 4935098
+ cause = cause.getCause();
if (cause instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException wrapping " +
"a ClassNotFoundException");
@@ -177,6 +209,8 @@ public class MissingClassTest {
ok = false;
} catch (IOException e) {
Throwable wrapped = e.getCause();
+ if (wrapped instanceof MARSHAL) // see CR 4935098
+ wrapped = wrapped.getCause();
if (wrapped instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException wrapping " +
"a ClassNotFoundException: " +
@@ -228,6 +262,8 @@ public class MissingClassTest {
ok = false;
} catch (IOException e) {
Throwable cause = e.getCause();
+ if (cause instanceof MARSHAL) // see CR 4935098
+ cause = cause.getCause();
if (cause instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException " +
"wrapping a ClassNotFoundException");
@@ -461,12 +497,13 @@ public class MissingClassTest {
while ((remain = deadline - System.currentTimeMillis()) >= 0) {
synchronized (result) {
if (result.failed
- || (result.knownCount == NNOTIFS
- && result.lostCount == NNOTIFS*2))
+ || (result.knownCount >= NNOTIFS
+ && result.lostCount >= NNOTIFS*2))
break;
result.wait(remain);
}
}
+ Thread.sleep(2); // allow any spurious extra notifs to arrive
if (result.failed) {
System.out.println("TEST FAILS: Notification strangeness");
return false;
@@ -476,6 +513,11 @@ public class MissingClassTest {
"got NOTIFS_LOST for unknown and " +
"unserializable ones");
return true;
+ } else if (result.knownCount >= NNOTIFS
+ || result.lostCount >= NNOTIFS*2) {
+ System.out.println("TEST FAILS: Received too many notifs: " +
+ "known=" + result.knownCount + "; lost=" + result.lostCount);
+ return false;
} else {
System.out.println("TEST FAILS: Timed out without receiving " +
"all notifs: known=" + result.knownCount +
--- a/test/javax/management/remote/mandatory/notif/AddRemoveTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/AddRemoveTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -33,10 +33,12 @@
*/
import java.net.MalformedURLException;
-import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
import javax.management.*;
import javax.management.remote.*;
+import javax.management.remote.rmi.RMIConnectorServer;
public class AddRemoveTest {
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
@@ -69,9 +71,16 @@ public class AddRemoveTest {
}
}
- private static boolean test(String proto)
+ private static boolean test(String proto) throws Exception {
+ boolean ok = test(proto, false);
+ ok &= test(proto, true);
+ return ok;
+ }
+
+ private static boolean test(String proto, boolean eventService)
throws Exception {
- System.out.println(">>> Test for protocol " + proto);
+ System.out.println(">>> Test for protocol " + proto + " with" +
+ (eventService ? "" : "out") + " event service");
JMXServiceURL u = new JMXServiceURL(proto, null, 0);
JMXConnectorServer server;
JMXServiceURL addr;
@@ -89,7 +98,10 @@ public class AddRemoveTest {
try {
// with a client listener, but close the server first
- server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
+ Map<String, String> env = Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
+ Boolean.toString(eventService));
+ server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
server.start();
addr = server.getAddress();
--- a/test/javax/management/remote/mandatory/notif/DiffHBTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/DiffHBTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -31,11 +31,12 @@
* @run main DiffHBTest
*/
-import java.net.MalformedURLException;
-import java.io.IOException;
-
+
+import java.util.Collections;
+import java.util.Map;
import javax.management.*;
import javax.management.remote.*;
+import javax.management.remote.rmi.RMIConnectorServer;
/**
* This test registeres an unique listener with two different handbacks,
@@ -48,11 +49,6 @@ public class DiffHBTest {
private static ObjectName delegateName;
private static ObjectName timerName;
- public static int received = 0;
- public static final int[] receivedLock = new int[0];
- public static Notification receivedNotif = null;
-
- public static Object receivedHB = null;
public static final String[] hbs = new String[] {"0", "1"};
public static void main(String[] args) throws Exception {
@@ -61,162 +57,174 @@ public class DiffHBTest {
delegateName = new ObjectName("JMImplementation:type=MBeanServerDelegate");
timerName = new ObjectName("MBean:name=Timer");
- boolean ok = true;
+ String errors = "";
+
for (int i = 0; i < protocols.length; i++) {
- try {
- if (!test(protocols[i])) {
- System.out.println(">>> Test failed for " + protocols[i]);
- ok = false;
+ final String s = test(protocols[i]);
+ if (s != null) {
+ if ("".equals(errors)) {
+ errors = "Failed to " + protocols[i] + ": "+s;
} else {
- System.out.println(">>> Test successed for " + protocols[i]);
- }
- } catch (Exception e) {
- System.out.println(">>> Test failed for " + protocols[i]);
- e.printStackTrace(System.out);
- ok = false;
- }
- }
-
- if (ok) {
- System.out.println(">>> Test passed");
+ errors = "\tFailed to " + protocols[i] + ": "+s;
+ }
+ }
+ }
+
+ if ("".equals(errors)) {
+ System.out.println(">>> Passed!");
} else {
- System.out.println(">>> TEST FAILED");
- System.exit(1);
- }
- }
-
- private static boolean test(String proto) throws Exception {
- System.out.println(">>> Test for protocol " + proto);
+ System.out.println(">>> Failed!");
+
+ throw new RuntimeException(errors);
+ }
+ }
+
+ private static String test(String proto) throws Exception {
+ String ret = null;
+ for (boolean eventService : new boolean[] {false, true}) {
+ String s = test(proto, eventService);
+ if (s != null) {
+ if (ret == null)
+ ret = s;
+ else
+ ret = ret + "; " + s;
+ }
+ }
+ return ret;
+ }
+
+ private static String test(String proto, boolean eventService)
+ throws Exception {
+ System.out.println(">>> Test for protocol " + proto + " with" +
+ (eventService ? "" : "out") + " event service");
JMXServiceURL u = new JMXServiceURL(proto, null, 0);
JMXConnectorServer server;
- JMXServiceURL addr;
JMXConnector client;
- MBeanServerConnection mserver;
-
- final NotificationListener dummyListener = new NotificationListener() {
- public void handleNotification(Notification n, Object o) {
- synchronized(receivedLock) {
- if (n == null) {
- System.out.println(">>> Got a null notification.");
- System.exit(1);
- }
-
- // check number
- if (received > 2) {
- System.out.println(">>> Expect to receive 2 notifs, but get "+received);
- System.exit(1);
- }
-
- if (received == 0) { // first time
- receivedNotif = n;
- receivedHB = o;
-
- if (!hbs[0].equals(o) && !hbs[1].equals(o)) {
- System.out.println(">>> Unkown handback: "+o);
- System.exit(1);
- }
- } else { // second time
- if (!receivedNotif.equals(n)) {
- System.out.println(">>> Not get same notif twice.");
- System.exit(1);
- } else if (!hbs[0].equals(o) && !hbs[1].equals(o)) { // validate handback
- System.out.println(">>> Unkown handback: "+o);
- System.exit(1);
- } else if (receivedHB.equals(o)) {
- System.out.println(">>> Got same handback twice: "+o);
- System.exit(1);
- }
- }
-
- ++received;
-
- if (received == 2) {
- receivedLock.notify();
- }
+
+ try {
+ Map<String, String> env = Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
+ Boolean.toString(eventService));
+ server =
+ JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
+ server.start();
+ JMXServiceURL addr = server.getAddress();
+ client = JMXConnectorFactory.connect(addr, null);
+ } catch (Exception e) {
+ // not support
+ System.out.println(">>> not support: " + proto);
+ return null;
+ }
+
+ MBeanServerConnection mserver = client.getMBeanServerConnection();
+
+ System.out.print(">>>\t");
+ for (int i=0; i<5; i++) {
+ System.out.print(i + "\t");
+ final MyListener dummyListener = new MyListener();
+ mserver.addNotificationListener(
+ delegateName, dummyListener, null, hbs[0]);
+ mserver.addNotificationListener(
+ delegateName, dummyListener, null, hbs[1]);
+
+ mserver.createMBean("javax.management.timer.Timer", timerName);
+
+ long remainingTime = waitingTime;
+ final long startTime = System.currentTimeMillis();
+
+ try {
+ synchronized(dummyListener) {
+ while (!dummyListener.done && remainingTime > 0) {
+ dummyListener.wait(remainingTime);
+ remainingTime = waitingTime -
+ (System.currentTimeMillis() - startTime);
}
- }
- };
-
- try {
- server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
- server.start();
-
- addr = server.getAddress();
- client = JMXConnectorFactory.newJMXConnector(addr, null);
- client.connect(null);
-
- mserver = client.getMBeanServerConnection();
-
- mserver.addNotificationListener(delegateName, dummyListener, null, hbs[0]);
- mserver.addNotificationListener(delegateName, dummyListener, null, hbs[1]);
-
- for (int i=0; i<20; i++) {
- synchronized(receivedLock) {
- received = 0;
- }
-
- mserver.createMBean("javax.management.timer.Timer", timerName);
-
- synchronized(receivedLock) {
- if (received != 2) {
- long remainingTime = waitingTime;
- final long startTime = System.currentTimeMillis();
-
- while (received != 2 && remainingTime > 0) {
- receivedLock.wait(remainingTime);
- remainingTime = waitingTime -
- (System.currentTimeMillis() - startTime);
- }
+
+ if (dummyListener.errorInfo != null) {
+ return dummyListener.errorInfo;
}
-
- if (received != 2) {
- System.out.println(">>> Expected 2 notifis, but received "+received);
-
- return false;
- }
- }
-
-
- synchronized(receivedLock) {
- received = 0;
- }
-
+ }
+ } finally {
+ //System.out.println("Unregister: "+i);
mserver.unregisterMBean(timerName);
-
- synchronized(receivedLock) {
- if (received != 2) {
-
- long remainingTime = waitingTime;
- final long startTime = System.currentTimeMillis();
-
- while (received != 2 && remainingTime >0) {
- receivedLock.wait(remainingTime);
- remainingTime = waitingTime -
- (System.currentTimeMillis() - startTime);
- }
- }
-
- if (received != 2) {
- System.out.println(">>> Expected 2 notifis, but received "+received);
-
- return false;
- }
- }
- }
-
- mserver.removeNotificationListener(delegateName, dummyListener);
-
- client.close();
-
- server.stop();
-
- } catch (MalformedURLException e) {
- System.out.println(">>> Skipping unsupported URL " + u);
- return true;
- }
-
- return true;
- }
-
- private final static long waitingTime = 10000;
+ mserver.removeNotificationListener(delegateName, dummyListener);
+ }
+ }
+
+ System.out.println("");
+ client.close();
+ server.stop();
+
+ return null;
+ }
+
+ private static class MyListener implements NotificationListener {
+ public boolean done = false;
+ public String errorInfo = null;
+
+ private int received = 0;
+ private MBeanServerNotification receivedNotif = null;
+ private Object receivedHB = null;
+ public void handleNotification(Notification n, Object o) {
+ if (!(n instanceof MBeanServerNotification)) {
+ failed("Received an unexpected notification: "+n);
+ return;
+ }
+
+ if (!hbs[0].equals(o) && !hbs[1].equals(o)) {
+ failed("Unkown handback: "+o);
+ return;
+ }
+
+ // what we need
+ final MBeanServerNotification msn = (MBeanServerNotification)n;
+ if (!(MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(
+ msn.getType())) ||
+ !msn.getMBeanName().equals(timerName)) {
+ return;
+ }
+
+ synchronized(this) {
+ received++;
+
+ if (received == 1) { // first time
+ receivedNotif = msn;
+ receivedHB = o;
+
+ return;
+ }
+
+ if (received > 2) {
+ failed("Expect to receive 2 notifs, but get "+received);
+
+ return;
+ }
+
+ // second time
+ if (receivedHB.equals(o)) {
+ failed("Got same handback twice: "+o);
+ } else if(!hbs[0].equals(o) && !hbs[1].equals(o)) {
+ failed("Unknown handback: "+o);
+ } else if (receivedNotif.getSequenceNumber() !=
+ msn.getSequenceNumber()) {
+ failed("expected to receive:\n"
+ +receivedNotif
+ +"\n but got\n"+msn);
+ }
+
+ // passed
+ done = true;
+ this.notify();
+ }
+ }
+
+ private void failed(String errorInfo) {
+ this.errorInfo = errorInfo;
+ done = true;
+
+ this.notify();
+ }
+ }
+
+ private final static long waitingTime = 2000;
}
--- a/test/javax/management/remote/mandatory/notif/EmptyDomainNotificationTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/EmptyDomainNotificationTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -29,11 +29,12 @@
* @author Shanliang JIANG
* @run clean EmptyDomainNotificationTest
* @run build EmptyDomainNotificationTest
- * @run main EmptyDomainNotificationTest
+ * @run main EmptyDomainNotificationTest classic
+ * @run main EmptyDomainNotificationTest event
*/
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collections;
+import java.util.Map;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerFactory;
@@ -46,6 +47,7 @@ import javax.management.remote.JMXConnec
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
+import javax.management.remote.rmi.RMIConnectorServer;
public class EmptyDomainNotificationTest {
@@ -80,11 +82,25 @@ public class EmptyDomainNotificationTest
public static void main(String[] args) throws Exception {
+ String type = args[0];
+ boolean eventService;
+ if (type.equals("classic"))
+ eventService = false;
+ else if (type.equals("event"))
+ eventService = true;
+ else
+ throw new IllegalArgumentException(type);
+
final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
- JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ Map<String, String> env = Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
+ Boolean.toString(eventService));
+
+ JMXConnectorServer server =
+ JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
server.start();
JMXConnector client = JMXConnectorFactory.connect(server.getAddress(), null);
--- a/test/javax/management/remote/mandatory/notif/ListenerScaleTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/ListenerScaleTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -51,18 +51,29 @@
* been compiled by the second.
*/
-import java.lang.management.ManagementFactory;
-import javax.management.*;
-import javax.management.remote.*;
-import java.util.concurrent.*;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerFactory;
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.rmi.RMIConnectorServer;
public class ListenerScaleTest {
private static final int WARMUP_WITH_ONE_MBEAN = 1000;
private static final int NOTIFS_TO_TIME = 100;
private static final int EXTRA_MBEANS = 20000;
- private static final MBeanServer mbs =
- ManagementFactory.getPlatformMBeanServer();
private static final ObjectName testObjectName;
static {
try {
@@ -87,7 +98,7 @@ public class ListenerScaleTest {
}
};
- private static final long timeNotif() {
+ private static final long timeNotif(MBeanServer mbs) {
try {
startTime = System.nanoTime();
nnotifs = 0;
@@ -117,12 +128,20 @@ public class ListenerScaleTest {
};
public static void main(String[] args) throws Exception {
- MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ test(false);
+ test(true);
+ }
+
+ private static void test(boolean eventService) throws Exception {
+ MBeanServer mbs = MBeanServerFactory.newMBeanServer();
Sender sender = new Sender();
mbs.registerMBean(sender, testObjectName);
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
+ Map<String, String> env = Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
+ Boolean.toString(eventService));
JMXConnectorServer cs =
- JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
cs.start();
JMXServiceURL addr = cs.getAddress();
JMXConnector cc = JMXConnectorFactory.connect(addr);
@@ -140,7 +159,7 @@ public class ListenerScaleTest {
mbsc.addNotificationListener(testObjectName, timingListener, null, null);
long singleMBeanTime = 0;
for (int i = 0; i < WARMUP_WITH_ONE_MBEAN; i++)
- singleMBeanTime = timeNotif();
+ singleMBeanTime = timeNotif(mbs);
if (singleMBeanTime == 0)
singleMBeanTime = 1;
System.out.println("Time with a single MBean: " + singleMBeanTime + "ns");
@@ -165,7 +184,7 @@ public class ListenerScaleTest {
}
System.out.println();
System.out.println("Timing a notification send now");
- long manyMBeansTime = timeNotif();
+ long manyMBeansTime = timeNotif(mbs);
System.out.println("Time with many MBeans: " + manyMBeansTime + "ns");
double ratio = (double) manyMBeansTime / singleMBeanTime;
if (ratio > 100.0)
--- a/test/javax/management/remote/mandatory/notif/NotifBufferSizePropertyNameTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/NotifBufferSizePropertyNameTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -31,11 +31,11 @@
* @run main NotifBufferSizePropertyNameTest
*/
-import java.io.IOException;
import java.util.*;
import javax.management.*;
import javax.management.remote.*;
+import javax.management.remote.rmi.RMIConnectorServer;
/**
* This class tests also the size of a server notification buffer.
@@ -87,6 +87,9 @@ public class NotifBufferSizePropertyName
private static void test(Map env) throws Exception {
final MBeanServer mbs = MBeanServerFactory.newMBeanServer();
+
+ env = new HashMap((env == null) ? Collections.emptyMap() : env);
+ env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
mbs.registerMBean(new NotificationEmitter(), oname);
JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(
--- a/test/javax/management/remote/mandatory/notif/NotifReconnectDeadlockTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/NotifReconnectDeadlockTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -22,7 +22,7 @@
*/
/*
- * @test NotifReconnectDeadlockTest
+ * @test
* @bug 6199899
* @summary Tests reconnection done by a fetching notif thread.
* @author Shanliang JIANG
@@ -31,11 +31,21 @@
* @run main NotifReconnectDeadlockTest
*/
-import java.io.IOException;
-import java.util.*;
-
-import javax.management.*;
-import javax.management.remote.*;
+import java.util.HashMap;
+import java.util.Map;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnectionNotification;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.rmi.RMIConnectorServer;
/**
* "This test checks for a bug whereby reconnection did not work if (a) it was
@@ -64,6 +74,7 @@ public class NotifReconnectDeadlockTest
Map env = new HashMap(2);
env.put("jmx.remote.x.server.connection.timeout", new Long(serverTimeout));
env.put("jmx.remote.x.client.connection.check.period", new Long(Long.MAX_VALUE));
+ env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
final MBeanServer mbs = MBeanServerFactory.newMBeanServer();
--- a/test/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -156,7 +156,8 @@ public class NotificationAccessControlle
List<Notification> received,
List<ObjectName> expected) {
if (received.size() != size) {
- echo("Error: expecting " + size + " notifications");
+ echo("Error: expecting " + size + " notifications, got " +
+ received.size());
return 1;
} else {
for (Notification n : received) {
--- a/test/javax/management/remote/mandatory/notif/NotificationBufferCreationTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/NotificationBufferCreationTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -32,6 +32,8 @@
*/
import java.net.MalformedURLException;
+import java.util.Collections;
+import java.util.Map;
import javax.management.MBeanServerFactory;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
@@ -44,6 +46,7 @@ import javax.management.remote.JMXConnec
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
+import javax.management.remote.rmi.RMIConnectorServer;
public class NotificationBufferCreationTest {
private static final MBeanServer mbs =
@@ -86,6 +89,8 @@ public class NotificationBufferCreationT
JMXServiceURL u = null;
try {
u = new JMXServiceURL(protocol, null, 0);
+ Map<String, String> env = Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
server =
JMXConnectorServerFactory.newJMXConnectorServer(u,
null,
--- a/test/javax/management/remote/mandatory/notif/NotificationBufferDeadlockTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/NotificationBufferDeadlockTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -35,7 +35,9 @@ import java.lang.reflect.Method;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.Vector;
import javax.management.*;
@@ -88,6 +90,7 @@ import javax.management.remote.*;
* If the logic for adding the notification buffer's listener is incorrect
* we could remove zero or two notifications from an MBean.
*/
+import javax.management.remote.rmi.RMIConnectorServer;
public class NotificationBufferDeadlockTest {
public static void main(String[] args) throws Exception {
System.out.println("Check no deadlock if notif sent while initial " +
@@ -109,7 +112,13 @@ public class NotificationBufferDeadlockT
}
private static void test(String proto) throws Exception {
- System.out.println("Testing protocol " + proto);
+ test(proto, false);
+ test(proto, true);
+ }
+
+ private static void test(String proto, boolean eventService) throws Exception {
+ System.out.println("Testing protocol " + proto + " with" +
+ (eventService ? "" : "out") + " event service");
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
ObjectName testName = newName();
DeadlockTest test = new DeadlockTest();
@@ -117,8 +126,11 @@ public class NotificationBufferDeadlockT
JMXServiceURL url = new JMXServiceURL("service:jmx:" + proto + ":///");
JMXConnectorServer cs;
try {
+ Map<String, String> env = Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
+ Boolean.toString(eventService));
cs =
- JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
} catch (MalformedURLException e) {
System.out.println("...protocol not supported, ignoring");
return;
--- a/test/javax/management/remote/mandatory/notif/NotificationEmissionTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/NotificationEmissionTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -29,11 +29,16 @@
* @author Luis-Miguel Alventosa
* @run clean NotificationEmissionTest
* @run build NotificationEmissionTest
- * @run main NotificationEmissionTest 1
- * @run main NotificationEmissionTest 2
- * @run main NotificationEmissionTest 3
- * @run main NotificationEmissionTest 4
- * @run main NotificationEmissionTest 5
+ * @run main NotificationEmissionTest 1 Classic
+ * @run main NotificationEmissionTest 2 Classic
+ * @run main NotificationEmissionTest 3 Classic
+ * @run main NotificationEmissionTest 4 Classic
+ * @run main NotificationEmissionTest 5 Classic
+ * @run main NotificationEmissionTest 1 EventService
+ * @run main NotificationEmissionTest 2 EventService
+ * @run main NotificationEmissionTest 3 EventService
+ * @run main NotificationEmissionTest 4 EventService
+ * @run main NotificationEmissionTest 5 EventService
*/
import java.io.File;
@@ -56,9 +61,15 @@ import javax.management.remote.JMXConnec
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXPrincipal;
import javax.management.remote.JMXServiceURL;
+import javax.management.remote.rmi.RMIConnectorServer;
import javax.security.auth.Subject;
public class NotificationEmissionTest {
+ private final boolean eventService;
+
+ public NotificationEmissionTest(boolean eventService) {
+ this.eventService = eventService;
+ }
public class CustomJMXAuthenticator implements JMXAuthenticator {
public Subject authenticate(Object credentials) {
@@ -102,7 +113,8 @@ public class NotificationEmissionTest {
List<Notification> received,
List<ObjectName> expected) {
if (received.size() != size) {
- echo("Error: expecting " + size + " notifications");
+ echo("Error: expecting " + size + " notifications, got " +
+ received.size());
return 1;
} else {
for (Notification n : received) {
@@ -216,8 +228,13 @@ public class NotificationEmissionTest {
//
final Map<String,Object> env = new HashMap<String,Object>();
env.put("jmx.remote.authenticator", new CustomJMXAuthenticator());
- if (prop)
+ env.put(RMIConnectorServer.EVENT_CLIENT_DELEGATE_FORWARDER,
+ Boolean.toString(eventService));
+ if (prop) {
+ echo("Setting jmx.remote.x.check.notification.emission to " +
+ propValue);
env.put("jmx.remote.x.check.notification.emission", propValue);
+ }
// Create the JMXServiceURL
//
@@ -282,9 +299,24 @@ public class NotificationEmissionTest {
new Object[] {2, nb3},
new String[] {"int", "javax.management.ObjectName"});
+ // If the check is effective and we're using policy.negative,
+ // then we should see the two notifs sent by nb2 (of which one
+ // has a getSource() that is nb3), but not the notif sent by nb1.
+ // Otherwise we should see all three notifs. If we're using the
+ // Event Service with a Security Manager then the logic to
+ // reapply the addNL permission test for every notification is
+ // always enabled, regardless of the value of
+ // jmx.remote.x.check.notification.emission. Otherwise, the
+ // test is only applied if that property is explicitly true.
+ int expectedNotifs =
+ ((prop || eventService) && sm && !policyPositive) ? 2 : 3;
+
// Wait for notifications to be emitted
//
- Thread.sleep(2000);
+ long deadline = System.currentTimeMillis() + 2000;
+ while (li.notifs.size() < expectedNotifs &&
+ System.currentTimeMillis() < deadline)
+ Thread.sleep(1);
// Remove notification listener
//
@@ -297,16 +329,10 @@ public class NotificationEmissionTest {
sources.add(nb2);
sources.add(nb3);
- if (prop && sm && !policyPositive) {
- // List must contain two notifs from sources nb2 and nb3
- //
- result = checkNotifs(2, li.notifs, sources);
- } else {
- // List must contain three notifs from sources nb1, nb2 and nb3
- //
- result = checkNotifs(3, li.notifs, sources);
- }
+ result = checkNotifs(expectedNotifs, li.notifs, sources);
if (result > 0) {
+ echo("...SecurityManager=" + sm + "; policy=" + policyPositive +
+ "; eventService=" + eventService);
return result;
}
} finally {
@@ -336,9 +362,18 @@ public class NotificationEmissionTest {
public static void main(String[] args) throws Exception {
echo("\n--- Check the emission of notifications " +
- "when a Security Manager is installed ---");
-
- NotificationEmissionTest net = new NotificationEmissionTest();
+ "when a Security Manager is installed [" +
+ args[1] + "] ---");
+
+ boolean eventService;
+ if (args[1].equals("Classic"))
+ eventService = false;
+ else if (args[1].equals("EventService"))
+ eventService = true;
+ else
+ throw new IllegalArgumentException(args[1]);
+
+ NotificationEmissionTest net = new NotificationEmissionTest(eventService);
int error = 0;
--- a/test/javax/management/remote/mandatory/notif/RMINotifTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/RMINotifTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -22,36 +22,53 @@
*/
/*
- * @test RMINotifTest.java
+ * @test
* @bug 7654321
- * @summary Tests to receive notifications for opened and closed connect
-ions
+ * @summary Tests to receive notifications for opened and closed connections
* @author sjiang
* @run clean RMINotifTest
* @run build RMINotifTest
- * @run main RMINotifTest
+ * @run main RMINotifTest classic
+ * @run main RMINotifTest event
*/
// java imports
//
-import java.io.IOException;
-import java.net.UnknownHostException;
-
-import java.rmi.*;
-import java.rmi.registry.*;
+
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.util.Collections;
+import java.util.Map;
import java.util.Random;
-
-// JMX imports
-//
-import javax.management.* ;
-
-import javax.management.remote.*;
-import javax.management.remote.rmi.*;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerFactory;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationFilterSupport;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
+import javax.management.remote.rmi.RMIConnectorServer;
public class RMINotifTest {
public static void main(String[] args) {
+ String eventService;
+ if (args[0].equals("classic"))
+ eventService = "false";
+ else if (args[0].equals("event"))
+ eventService = "true";
+ else
+ throw new IllegalArgumentException(args[0]);
+
try {
// create a rmi registry
Registry reg = null;
@@ -88,9 +105,10 @@ public class RMINotifTest {
"/jndi/rmi://:" + port + "/server" + port);
System.out.println("RMIConnectorServer address " + url);
+ Map<String, String> env = Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, eventService);
JMXConnectorServer sServer =
- JMXConnectorServerFactory.newJMXConnectorServer(url, null,
- null);
+ JMXConnectorServerFactory.newJMXConnectorServer(url, env, null);
ObjectInstance ss = server.registerMBean(sServer, new ObjectName("Default:name=RmiConnectorServer"));
--- a/test/javax/management/remote/mandatory/notif/UnexpectedNotifTest.java Thu Jul 31 14:20:11 2008 +0200
+++ b/test/javax/management/remote/mandatory/notif/UnexpectedNotifTest.java Thu Jul 31 15:31:13 2008 +0200
@@ -32,68 +32,88 @@
* @run main UnexpectedNotifTest
*/
-// java imports
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerFactory;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
//
-import java.io.IOException;
-
-// JMX imports
-//
-import javax.management.*;
-import javax.management.remote.*;
+import javax.management.remote.rmi.RMIConnectorServer;
public class UnexpectedNotifTest {
public static void main(String[] args) throws Exception {
- String[] protos = null;
+ List<String> protos = new ArrayList<String>();
+ protos.add("rmi");
try {
Class.forName("javax.management.remote.jmxmp.JMXMPConnectorServer");
- protos = new String[2];
- protos[0] = "rmi";
- protos[1] = "jmxmp";
+ protos.add("jmxmp");
} catch (ClassNotFoundException e) {
- protos = new String[1];
- protos[0] = "rmi";
- }
- for (int i = 0; i < protos.length; i++) {
- System.out.println("Unexpected notifications test for protocol " +
- protos[i]);
- MBeanServer mbs = null;
- try {
- // Create a MBeanServer
- //
- mbs = MBeanServerFactory.createMBeanServer();
-
- // Create a NotificationEmitter MBean
- //
- mbean = new ObjectName ("Default:name=NotificationEmitter");
- mbs.registerMBean(new NotificationEmitter(), mbean);
-
- // Create a connector server
- //
- url = new JMXServiceURL("service:jmx:" + protos[i] + "://");
- server = JMXConnectorServerFactory.newJMXConnectorServer(url,
- null,
- mbs);
-
- mbs.registerMBean(
- server,
- new ObjectName("Default:name=ConnectorServer"));
-
- server.start();
-
- url = server.getAddress();
-
- for (int j = 0; j < 2; j++) {
- test();
- }
- } finally {
- // Stop server
- //
- server.stop();
- // Release the MBeanServer
- //
- MBeanServerFactory.releaseMBeanServer(mbs);
- }
+ // OK: JMXMP not present so don't test it.
+ }
+ for (String proto : protos) {
+ test(proto, false);
+ test(proto, true);
+ }
+ }
+
+ private static void test(String proto, boolean eventService)
+ throws Exception {
+ System.out.println("Unexpected notifications test for protocol " +
+ proto + " with" +
+ (eventService ? "" : "out") + " event service");
+ MBeanServer mbs = null;
+ try {
+ // Create a MBeanServer
+ //
+ mbs = MBeanServerFactory.createMBeanServer();
+
+ // Create a NotificationEmitter MBean
+ //
+ mbean = new ObjectName ("Default:name=NotificationEmitter");
+ mbs.registerMBean(new NotificationEmitter(), mbean);
+
+ // Create a connector server
+ //
+ url = new JMXServiceURL("service:jmx:" + proto + "://");
+ Map<String, String> env = Collections.singletonMap(
+ RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
+ Boolean.toString(eventService));
+
+ server = JMXConnectorServerFactory.newJMXConnectorServer(url,
+ env,
+ mbs);
+
+ mbs.registerMBean(
+ server,
+ new ObjectName("Default:name=ConnectorServer"));
+
+ server.start();
+
+ url = server.getAddress();
+
+ for (int j = 0; j < 2; j++) {
+ test();
+ }
+ } finally {
+ // Stop server
+ //
+ server.stop();
+ // Release the MBeanServer
+ //
+ MBeanServerFactory.releaseMBeanServer(mbs);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java Thu Jul 31 15:31:13 2008 +0200
@@ -0,0 +1,77 @@
+/*
+ * 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.event;
+
+import com.sun.jmx.remote.util.ClassLogger;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class DaemonThreadFactory implements ThreadFactory {
+ public DaemonThreadFactory(String nameTemplate) {
+ this(nameTemplate, null);
+ }
+
+ // nameTemplate should be a format with %d in it, which will be replaced
+ // by a sequence number of threads created by this factory.
+ public DaemonThreadFactory(String nameTemplate, ThreadGroup threadGroup) {
+ if (logger.debugOn()) {
+ logger.debug("DaemonThreadFactory",
+ "Construct a new daemon factory: "+nameTemplate);
+ }
+
+ if (threadGroup == null) {
+ SecurityManager s = System.getSecurityManager();
+ threadGroup = (s != null) ? s.getThreadGroup() :
+ Thread.currentThread().getThreadGroup();
+ }
+
+ this.nameTemplate = nameTemplate;
+ this.threadGroup = threadGroup;
+ }
+
+ public Thread newThread(Runnable r) {
+ final String name =
+ String.format(nameTemplate, threadNumber.getAndIncrement());
+ Thread t = new Thread(threadGroup, r, name, 0);
+ t.setDaemon(true);
+ if (t.getPriority() != Thread.NORM_PRIORITY)
+ t.setPriority(Thread.NORM_PRIORITY);
+
+ if (logger.debugOn()) {
+ logger.debug("newThread",
+ "Create a new daemon thread with the name "+t.getName());
+ }
+
+ return t;
+ }
+
+ private final String nameTemplate;
+ private final ThreadGroup threadGroup;
+ private final AtomicInteger threadNumber = new AtomicInteger(1);
+
+ private static final ClassLogger logger =
+ new ClassLogger("com.sun.jmx.event", "DaemonThreadFactory");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/event/EventBuffer.java Thu Jul 31 15:31:13 2008 +0200
@@ -0,0 +1,252 @@
+/*
+ * 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.event;
+
+import com.sun.jmx.remote.util.ClassLogger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.management.remote.NotificationResult;
+import javax.management.remote.TargetedNotification;
+
+public class EventBuffer {
+
+ public EventBuffer() {
+ this(Integer.MAX_VALUE, null);
+ }
+
+ public EventBuffer(int capacity) {
+ this(capacity, new ArrayList<TargetedNotification>());
+ }
+
+ public EventBuffer(int capacity, final List<TargetedNotification> list) {
+ if (logger.traceOn()) {
+ logger.trace("EventBuffer", "New buffer with the capacity: "
+ +capacity);
+ }
+ if (capacity < 1) {
+ throw new IllegalArgumentException(
+ "The capacity must be bigger than 0");
+ }
+
+ if (list == null) {
+ throw new NullPointerException("Null list.");
+ }
+
+ this.capacity = capacity;
+ this.list = list;
+ }
+
+ public void add(TargetedNotification tn) {
+ if (logger.traceOn()) {
+ logger.trace("add", "Add one notif.");
+ }
+
+ synchronized(lock) {
+ if (list.size() == capacity) { // have to throw one
+ passed++;
+ list.remove(0);
+
+ if (logger.traceOn()) {
+ logger.trace("add", "Over, remove the oldest one.");
+ }
+ }
+
+ list.add(tn);
+ lock.notify();
+ }
+ }
+
+ public void add(TargetedNotification[] tns) {
+ if (tns == null || tns.length == 0) {
+ return;
+ }
+
+ if (logger.traceOn()) {
+ logger.trace("add", "Add notifs: "+tns.length);
+ }
+
+ synchronized(lock) {
+ final int d = list.size() - capacity + tns.length;
+ if (d > 0) { // have to throw
+ passed += d;
+ if (logger.traceOn()) {
+ logger.trace("add",
+ "Over, remove the oldest: "+d);
+ }
+ if (tns.length <= capacity){
+ list.subList(0, d).clear();
+ } else {
+ list.clear();
+ TargetedNotification[] tmp =
+ new TargetedNotification[capacity];
+ System.arraycopy(tns, tns.length-capacity, tmp, 0, capacity);
+ tns = tmp;
+ }
+ }
+
+ Collections.addAll(list,tns);
+ lock.notify();
+ }
+ }
+
+ public NotificationResult fetchNotifications(long startSequenceNumber,
+ long timeout,
+ int maxNotifications) {
+ if (logger.traceOn()) {
+ logger.trace("fetchNotifications",
+ "Being called: "
+ +startSequenceNumber+" "
+ +timeout+" "+maxNotifications);
+ }
+ if (startSequenceNumber < 0 ||
+ timeout < 0 ||
+ maxNotifications < 0) {
+ throw new IllegalArgumentException("Negative value.");
+ }
+
+ TargetedNotification[] tns = new TargetedNotification[0];
+ long earliest = startSequenceNumber < passed ?
+ passed : startSequenceNumber;
+ long next = earliest;
+
+ final long startTime = System.currentTimeMillis();
+ long toWait = timeout;
+ synchronized(lock) {
+ int toSkip = (int)(startSequenceNumber - passed);
+
+ // skip those before startSequenceNumber.
+ while (!closed && toSkip > 0) {
+ toWait = timeout - (System.currentTimeMillis() - startTime);
+ if (list.size() == 0) {
+ if (toWait <= 0) {
+ // the notification of startSequenceNumber
+ // does not arrive yet.
+ return new NotificationResult(startSequenceNumber,
+ startSequenceNumber,
+ new TargetedNotification[0]);
+ }
+
+ waiting(toWait);
+ continue;
+ }
+
+ if (toSkip <= list.size()) {
+ list.subList(0, toSkip).clear();
+ passed += toSkip;
+
+ break;
+ } else {
+ passed += list.size();
+ toSkip -= list.size();
+
+ list.clear();
+ }
+ }
+
+ earliest = passed;
+
+ if (list.size() == 0) {
+ toWait = timeout - (System.currentTimeMillis() - startTime);
+
+ waiting(toWait);
+ }
+
+ if (list.size() == 0) {
+ tns = new TargetedNotification[0];
+ } else if (list.size() <= maxNotifications) {
+ tns = list.toArray(new TargetedNotification[0]);
+ } else {
+ tns = new TargetedNotification[maxNotifications];
+ for (int i=0; i<maxNotifications; i++) {
+ tns[i] = list.get(i);
+ }
+ }
+
+ next = earliest + tns.length;
+ }
+
+ if (logger.traceOn()) {
+ logger.trace("fetchNotifications",
+ "Return: "+earliest+" "+next+" "+tns.length);
+ }
+
+ return new NotificationResult(earliest, next, tns);
+ }
+
+ public int size() {
+ return list.size();
+ }
+
+ public void addLost(long nb) {
+ synchronized(lock) {
+ passed += nb;
+ }
+ }
+
+ public void close() {
+ if (logger.traceOn()) {
+ logger.trace("clear", "done");
+ }
+
+ synchronized(lock) {
+ list.clear();
+ closed = true;
+ lock.notifyAll();
+ }
+ }
+
+
+ // -------------------------------------------
+ // private classes
+ // -------------------------------------------
+ private void waiting(long timeout) {
+ final long startTime = System.currentTimeMillis();
+ long toWait = timeout;
+ synchronized(lock) {
+ while (!closed && list.size() == 0 && toWait > 0) {
+ try {
+ lock.wait(toWait);
+
+ toWait = timeout - (System.currentTimeMillis() - startTime);
+ } catch (InterruptedException ire) {
+ logger.trace("waiting", ire);
+ break;
+ }
+ }
+ }
+ }
+
+ private final int capacity;
+ private final List<TargetedNotification> list;
+ private boolean closed;
+
+ private long passed = 0;
+ private final int[] lock = new int[0];
+
+ private static final ClassLogger logger =
+ new ClassLogger("javax.management.event", "EventBuffer");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/event/EventClientFactory.java Thu Jul 31 15:31:13 2008 +0200
@@ -0,0 +1,46 @@
+/*
+ * 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.event;
+
+import javax.management.event.*;
+
+/**
+ * Implemented by objects which are using an {@link EventClient} to
+ * subscribe for Notifications.
+ *
+ */
+public interface EventClientFactory {
+ /**
+ * Returns the {@code EventClient} that the object implementing this
+ * interface uses to subscribe for Notifications. This method returns
+ * {@code null} if no {@code EventClient} can be used - e.g. because
+ * the underlying server does not have any {@link EventDelegate}.
+ *
+ * @return an {@code EventClient} or {@code null}.
+ **/
+ public EventClient getEventClient();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/event/EventConnection.java Thu Jul 31 15:31:13 2008 +0200
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2002-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.event;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import javax.management.MBeanServerConnection;
+import javax.management.event.EventClient;
+import javax.management.event.EventClientDelegate;
+import javax.management.event.EventConsumer;
+import javax.management.event.NotificationManager;
+
+/**
+ * Override the methods related to the notification to use the
+ * Event service.
+ */
+public interface EventConnection extends MBeanServerConnection, EventConsumer {
+ public EventClient getEventClient();
+
+ public static class Factory {
+ public static EventConnection make(
+ final MBeanServerConnection mbsc,
+ final EventClient eventClient)
+ throws IOException {
+ if (!mbsc.isRegistered(EventClientDelegate.OBJECT_NAME)) {
+ throw new IOException(
+ "The server does not support the event service.");
+ }
+ InvocationHandler ih = new InvocationHandler() {
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ Class<?> intf = method.getDeclaringClass();
+ try {
+ if (intf.isInstance(eventClient))
+ return method.invoke(eventClient, args);
+ else
+ return method.invoke(mbsc, args);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+ };
+ // It is important to declare NotificationManager.class first
+ // in the array below, so that the relevant addNL and removeNL
+ // methods will show up with method.getDeclaringClass() as
+ // being from that interface and not MBeanServerConnection.
+ return (EventConnection) Proxy.newProxyInstance(
+ NotificationManager.class.getClassLoader(),
+ new Class<?>[] {
+ NotificationManager.class, EventConnection.class,
+ },
+ ih);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/event/EventParams.java Thu Jul 31 15:31:13 2008 +0200
@@ -0,0 +1,65 @@
+/*
+ * 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.event;
+
+import com.sun.jmx.mbeanserver.GetPropertyAction;
+import com.sun.jmx.remote.util.ClassLogger;
+import java.security.AccessController;
+import javax.management.event.EventClient;
+
+/**
+ *
+ * @author sjiang
+ */
+public class EventParams {
+ public static final String DEFAULT_LEASE_TIMEOUT =
+ "com.sun.event.lease.time";
+
+
+ @SuppressWarnings("cast") // cast for jdk 1.5
+ public static long getLeaseTimeout() {
+ long timeout = EventClient.DEFAULT_LEASE_TIMEOUT;
+ try {
+ final GetPropertyAction act =
+ new GetPropertyAction(DEFAULT_LEASE_TIMEOUT);
+ final String s = (String)AccessController.doPrivileged(act);
+ if (s != null) {
+ timeout = Long.parseLong(s);
+ }
+ } catch (RuntimeException e) {
+ logger.fine("getLeaseTimeout", "exception getting property", e);
+ }
+
+ return timeout;
+ }
+
+ /** Creates a new instance of EventParams */
+ private EventParams() {
+ }
+
+ private static final ClassLogger logger =
+ new ClassLogger("javax.management.event", "EventParams");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/event/LeaseManager.java Thu Jul 31 15:31:13 2008 +0200
@@ -0,0 +1,146 @@
+/*
+ * 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.event;
+
+import com.sun.jmx.remote.util.ClassLogger;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * <p>Manage a renewable lease. The lease can be renewed indefinitely
+ * but if the lease runs to its current expiry date without being renewed
+ * then the expiry callback is invoked. If the lease has already expired
+ * when renewal is attempted then the lease method returns zero.</p>
+ * @author sjiang
+ * @author emcmanus
+ */
+// The synchronization logic of this class is tricky to deal correctly with the
+// case where the lease expires at the same time as the |lease| or |stop| method
+// is called. If the lease is active then the field |scheduled| represents
+// the expiry task; otherwise |scheduled| is null. Renewing or stopping the
+// lease involves canceling this task and setting |scheduled| either to a new
+// task (to renew) or to null (to stop).
+//
+// Suppose the expiry task runs at the same time as the |lease| method is called.
+// If the task enters its synchronized block before the method starts, then
+// it will set |scheduled| to null and the method will return 0. If the method
+// starts before the task enters its synchronized block, then the method will
+// cancel the task which will see that when it later enters the block.
+// Similar reasoning applies to the |stop| method. It is not expected that
+// different threads will call |lease| or |stop| simultaneously, although the
+// logic should be correct then too.
+public class LeaseManager {
+ public LeaseManager(Runnable callback) {
+ this(callback, EventParams.getLeaseTimeout());
+ }
+
+ public LeaseManager(Runnable callback, long timeout) {
+ if (logger.traceOn()) {
+ logger.trace("LeaseManager", "new manager with lease: "+timeout);
+ }
+ if (callback == null) {
+ throw new NullPointerException("Null callback.");
+ }
+ if (timeout <= 0)
+ throw new IllegalArgumentException("Timeout must be positive: " + timeout);
+
+ this.callback = callback;
+ schedule(timeout);
+ }
+
+ /**
+ * <p>Renew the lease for the given time. The new time can be shorter
+ * than the previous one, in which case the lease will expire earlier
+ * than it would have.</p>
+ *
+ * <p>Calling this method after the lease has expired will return zero
+ * immediately and have no other effect.</p>
+ *
+ * @param timeout the new lifetime. If zero, the lease
+ * will expire immediately.
+ */
+ public synchronized long lease(long timeout) {
+ if (logger.traceOn()) {
+ logger.trace("lease", "new lease to: "+timeout);
+ }
+
+ if (timeout < 0)
+ throw new IllegalArgumentException("Negative lease: " + timeout);
+
+ if (scheduled == null)
+ return 0L;
+
+ scheduled.cancel(false);
+
+ if (logger.traceOn())
+ logger.trace("lease", "start lease: "+timeout);
+ schedule(timeout);
+
+ return timeout;
+ }
+
+ private class Expire implements Runnable {
+ ScheduledFuture<?> task;
+
+ public void run() {
+ synchronized (LeaseManager.this) {
+ if (task.isCancelled())
+ return;
+ scheduled = null;
+ }
+ callback.run();
+ }
+ }
+
+ private synchronized void schedule(long timeout) {
+ Expire expire = new Expire();
+ scheduled = executor.schedule(expire, timeout, TimeUnit.MILLISECONDS);
+ expire.task = scheduled;
+ }
+
+ /**
+ * <p>Cancel the lease without calling the expiry callback.</p>
+ */
+ public synchronized void stop() {
+ logger.trace("stop", "canceling lease");
+ scheduled.cancel(false);
+ scheduled = null;
+ }
+
+ private final Runnable callback;
+ private ScheduledFuture scheduled; // If null, the lease has expired.
+
+ private final ScheduledExecutorService executor
+ = Executors.newScheduledThreadPool(1,
+ new DaemonThreadFactory("LeaseManager"));
+
+ private static final ClassLogger logger =
+ new ClassLogger("javax.management.event", "LeaseManager");
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/event/LeaseRenewer.java Thu Jul 31 15:31:13 2008 +0200
@@ -0,0 +1,143 @@
+/*
+ * 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.event;
+
+import com.sun.jmx.remote.util.ClassLogger;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *
+ * @author sjiang
+ */
+public class LeaseRenewer {
+ public LeaseRenewer(ScheduledExecutorService scheduler, Callable<Long> doRenew) {
+ if (logger.traceOn()) {
+ logger.trace("LeaseRenewer", "New LeaseRenewer.");
+ }
+
+ if (doRenew == null) {
+ throw new NullPointerException("Null job to call server.");
+ }
+
+ this.doRenew = doRenew;
+ nextRenewTime = System.currentTimeMillis();
+
+ this.scheduler = scheduler;
+ future = this.scheduler.schedule(myRenew, 0, TimeUnit.MILLISECONDS);
+ }
+
+ public void close() {
+ if (logger.traceOn()) {
+ logger.trace("close", "Close the lease.");
+ }
+
+ synchronized(lock) {
+ if (closed) {
+ return;
+ } else {
+ closed = true;
+ }
+ }
+
+ try {
+ future.cancel(false); // not interrupt if running
+ } catch (Exception e) {
+ // OK
+ if (logger.debugOn()) {
+ logger.debug("close", "Failed to cancel the leasing job.", e);
+ }
+ }
+ }
+
+ public boolean closed() {
+ synchronized(lock) {
+ return closed;
+ }
+ }
+
+ // ------------------------------
+ // private
+ // ------------------------------
+ private final Runnable myRenew = new Runnable() {
+ public void run() {
+ synchronized(lock) {
+ if (closed()) {
+ return;
+ }
+ }
+
+ long next = nextRenewTime - System.currentTimeMillis();
+ if (next < MIN_MILLIS) {
+ try {
+ if (logger.traceOn()) {
+ logger.trace("myRenew-run", "");
+ }
+ next = doRenew.call().longValue();
+
+ } catch (Exception e) {
+ logger.fine("myRenew-run", "Failed to renew lease", e);
+ close();
+ }
+
+ if (next > 0 && next < Long.MAX_VALUE) {
+ next = next/2;
+ next = (next < MIN_MILLIS) ? MIN_MILLIS : next;
+ } else {
+ close();
+ }
+ }
+
+ nextRenewTime = System.currentTimeMillis() + next;
+
+ if (logger.traceOn()) {
+ logger.trace("myRenew-run", "Next leasing: "+next);
+ }
+
+ synchronized(lock) {
+ if (!closed) {
+ future = scheduler.schedule(this, next, TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+ };
+
+ private final Callable<Long> doRenew;
+ private ScheduledFuture future;
+ private boolean closed = false;
+ private long nextRenewTime;
+
+ private final int[] lock = new int[0];
+
+ private final ScheduledExecutorService scheduler;
+
+ private static final long MIN_MILLIS = 50;
+
+ private static final ClassLogger logger =
+ new ClassLogger("javax.management.event", "LeaseRenewer");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/event/ReceiverBuffer.java Thu Jul 31 15:31:13 2008 +0200
@@ -0,0 +1,97 @@
+/*
+ * 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.event;
+
+import com.sun.jmx.remote.util.ClassLogger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.management.remote.NotificationResult;
+import javax.management.remote.TargetedNotification;
+
+
+public class ReceiverBuffer {
+ public void addNotifs(NotificationResult nr) {
+ if (nr == null) {
+ return;
+ }
+
+ TargetedNotification[] tns = nr.getTargetedNotifications();
+
+ if (logger.traceOn()) {
+ logger.trace("addNotifs", "" + tns.length);
+ }
+
+ long impliedStart = nr.getEarliestSequenceNumber();
+ final long missed = impliedStart - start;
+ start = nr.getNextSequenceNumber();
+
+ if (missed > 0) {
+ if (logger.traceOn()) {
+ logger.trace("addNotifs",
+ "lost: "+missed);
+ }
+
+ lost += missed;
+ }
+
+ Collections.addAll(notifList, nr.getTargetedNotifications());
+ }
+
+ public TargetedNotification[] removeNotifs() {
+ if (logger.traceOn()) {
+ logger.trace("removeNotifs", String.valueOf(notifList.size()));
+ }
+
+ if (notifList.size() == 0) {
+ return null;
+ }
+
+ TargetedNotification[] ret = notifList.toArray(
+ new TargetedNotification[]{});
+ notifList.clear();
+
+ return ret;
+ }
+
+ public int size() {
+ return notifList.size();
+ }
+
+ public int removeLost() {
+ int ret = lost;
+ lost = 0;
+ return ret;
+ }
+
+ private List<TargetedNotification> notifList
+ = new ArrayList<TargetedNotification>();
+ private long start = 0;
+ private int lost = 0;
+
+ private static final ClassLogger logger =
+ new ClassLogger("javax.management.event", "ReceiverBuffer");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java Thu Jul 31 15:31:13 2008 +0200
@@ -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 com.sun.jmx.event;
+
+import com.sun.jmx.remote.util.ClassLogger;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * <p>A task that is repeatedly run by an Executor. The task will be
+ * repeated as long as the {@link #isSuspended()} method returns true. Once
+ * that method returns false, the task is no longer executed until someone
+ * calls {@link #resume()}.</p>
+ * @author sjiang
+ */
+public abstract class RepeatedSingletonJob implements Runnable {
+ public RepeatedSingletonJob(Executor executor) {
+ if (executor == null) {
+ throw new NullPointerException("Null executor!");
+ }
+
+ this.executor = executor;
+ }
+
+ public boolean isWorking() {
+ return working;
+ }
+
+ public void resume() {
+
+ synchronized(this) {
+ if (!working) {
+ if (logger.traceOn()) {
+ logger.trace("resume", "");
+ }
+ working = true;
+ execute();
+ }
+ }
+ }
+
+ public abstract void task();
+ public abstract boolean isSuspended();
+
+ public void run() {
+ if (logger.traceOn()) {
+ logger.trace("run", "execute the task");
+ }
+ try {
+ task();
+ } catch (Exception e) {
+ // A correct task() implementation should not throw exceptions.
+ // It may cause isSuspended() to start returning true, though.
+ logger.trace("run", "failed to execute the task", e);
+ }
+
+ synchronized(this) {
+ if (!isSuspended()) {
+ execute();
+ } else {
+ if (logger.traceOn()) {
+ logger.trace("run", "suspend the task");
+ }
+ working = false;
+ }
+ }
+
+ }
+
+ private void execute() {
+ try {
+ executor.execute(this);
+ } catch (RejectedExecutionException e) {
+ logger.warning(
+ "setEventReceiver", "Executor threw exception", e);
+ throw new RejectedExecutionException(
+ "Executor.execute threw exception -" +
+ "should not be possible", e);
+ // User-supplied Executor should not be configured in a way that
+ // might cause this exception, for example if it is shared between
+ // several client objects and doesn't have capacity for one job
+ // from each one. CR 6732037 will add text to the spec explaining
+ // the problem. The rethrown exception will propagate either out
+ // of resume() to user code, or out of run() to the Executor
+ // (which will probably ignore it).
+ }
+ }
+
+ private boolean working = false;
+ private final Executor executor;
+
+ private static final ClassLogger logger =
+ new ClassLogger("javax.management.event", "RepeatedSingletonJob");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java Thu Jul 31 15:31:13 2008 +0200
@@ -0,0 +1,1341 @@
+/*
+ * 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.interceptor;
+
+import com.sun.jmx.mbeanserver.Util;
+import java.io.ObjectInputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.DynamicWrapperMBean;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMRuntimeException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+import javax.management.QueryEval;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import javax.management.loading.ClassLoaderRepository;
+
+/**
+ * <p>Base class for custom implementations of the {@link MBeanServer}
+ * interface. The commonest use of this class is as the {@linkplain
+ * JMXNamespace#getSourceServer() source server} for a {@link
+ * JMXNamespace}, although this class can be used anywhere an {@code
+ * MBeanServer} instance is required. Note that the usual ways to
+ * obtain an {@code MBeanServer} instance are either to use {@link
+ * java.lang.management.ManagementFactory#getPlatformMBeanServer()
+ * ManagementFactory.getPlatformMBeanServer()} or to use the {@code
+ * newMBeanServer} or {@code createMBeanServer} methods from {@link
+ * javax.management.MBeanServerFactory MBeanServerFactory}. {@code
+ * MBeanServerSupport} is for certain cases where those are not
+ * appropriate.</p>
+ *
+ * <p>There are two main use cases for this class: <a
+ * href="#special-purpose">special-purpose MBeanServer implementations</a>,
+ * and <a href="#virtual">namespaces containing Virtual MBeans</a>. The next
+ * sections explain these use cases.</p>
+ *
+ * <p>In the simplest case, a subclass needs to implement only two methods:</p>
+ *
+ * <ul>
+ * <li>
+ * {@link #getNames getNames} which returns the name of
+ * all MBeans handled by this {@code MBeanServer}.
+ * </li>
+ * <li>
+ * {@link #getDynamicMBeanFor getDynamicMBeanFor} which returns a
+ * {@link DynamicMBean} that can be used to invoke operations and
+ * obtain meta data (MBeanInfo) on a given MBean.
+ * </li>
+ * </ul>
+ *
+ * <p>Subclasses can create such {@link DynamicMBean} MBeans on the fly - for
+ * instance, using the class {@link javax.management.StandardMBean}, just for
+ * the duration of an MBeanServer method call.</p>
+ *
+ * <h4 id="special-purpose">Special-purpose MBeanServer implementations</h4>
+ *
+ * <p>In some cases
+ * the general-purpose {@code MBeanServer} that you get from
+ * {@link javax.management.MBeanServerFactory MBeanServerFactory} is not
+ * appropriate. You might need different security checks, or you might
+ * want a mock {@code MBeanServer} suitable for use in tests, or you might
+ * want a simplified and optimized {@code MBeanServer} for a special purpose.</p>
+ *
+ * <p>As an example of a special-purpose {@code MBeanServer}, the class {@link
+ * javax.management.QueryNotificationFilter QueryNotificationFilter} constructs
+ * an {@code MBeanServer} instance every time it filters a notification,
+ * with just one MBean that represents the notification. Although it could
+ * use {@code MBeanServerFactory.newMBeanServer}, a special-purpose {@code
+ * MBeanServer} will be quicker to create, use less memory, and have simpler
+ * methods that execute faster.</p>
+ *
+ * <p>Here is an example of a special-purpose {@code MBeanServer}
+ * implementation that contains exactly one MBean, which is specified at the
+ * time of creation.</p>
+ *
+ * <pre>
+ * public class SingletonMBeanServer extends MBeanServerSupport {
+ * private final ObjectName objectName;
+ * private final DynamicMBean mbean;
+ *
+ * public SingletonMBeanServer(ObjectName objectName, DynamicMBean mbean) {
+ * this.objectName = objectName;
+ * this.mbean = mbean;
+ * }
+ *
+ * &#64;Override
+ * protected {@code Set<ObjectName>} {@link #getNames getNames}() {
+ * return Collections.singleton(objectName);
+ * }
+ *
+ * &#64;Override
+ * public DynamicMBean {@link #getDynamicMBeanFor
+ * getDynamicMBeanFor}(ObjectName name)
+ * throws InstanceNotFoundException {
+ * if (objectName.equals(name))
+ * return mbean;
+ * else
+ * throw new InstanceNotFoundException(name);
+ * } <