From 192678a7934c1c07a985c6b4db093f24b3bc9ffb Mon Sep 17 00:00:00 2001
From: Daniel Fuchs
Date: Thu, 31 Jul 2008 12:41:35 +0200
Subject: [PATCH 01/13] 6730926: Document that create/registerMBean can throw
RuntimeMBeanException from postRegister
Reviewed-by: emcmanus
---
.../DefaultMBeanServerInterceptor.java | 53 +-
.../javax/management/MBeanRegistration.java | 25 +-
.../classes/javax/management/MBeanServer.java | 19 +
.../management/MBeanServerConnection.java | 99 +++-
.../MBeanServer/PostExceptionTest.java | 516 ++++++++++++++++++
5 files changed, 693 insertions(+), 19 deletions(-)
create mode 100644 jdk/test/javax/management/MBeanServer/PostExceptionTest.java
diff --git a/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java b/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java
index 99b10ad1e8c..8bd6cba27ce 100644
--- a/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java
+++ b/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java
@@ -453,11 +453,12 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
final ResourceContext context =
unregisterFromRepository(resource, instance, name);
-
- if (instance instanceof MBeanRegistration)
- postDeregisterInvoke((MBeanRegistration) instance);
-
- context.done();
+ try {
+ if (instance instanceof MBeanRegistration)
+ postDeregisterInvoke(name,(MBeanRegistration) instance);
+ } finally {
+ context.done();
+ }
}
public ObjectInstance getObjectInstance(ObjectName name)
@@ -989,10 +990,12 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
registerFailed = false;
registered = true;
} finally {
- postRegister(mbean, registered, registerFailed);
+ try {
+ postRegister(logicalName, mbean, registered, registerFailed);
+ } finally {
+ if (registered) context.done();
+ }
}
-
- context.done();
return new ObjectInstance(logicalName, classname);
}
@@ -1051,7 +1054,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
private static void postRegister(
- DynamicMBean mbean, boolean registrationDone, boolean registerFailed) {
+ ObjectName logicalName, DynamicMBean mbean,
+ boolean registrationDone, boolean registerFailed) {
if (registerFailed && mbean instanceof DynamicMBean2)
((DynamicMBean2) mbean).registerFailed();
@@ -1059,11 +1063,19 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
if (mbean instanceof MBeanRegistration)
((MBeanRegistration) mbean).postRegister(registrationDone);
} catch (RuntimeException e) {
+ MBEANSERVER_LOGGER.fine("While registering MBean ["+logicalName+
+ "]: " + "Exception thrown by postRegister: " +
+ "rethrowing <"+e+">, but keeping the MBean registered");
throw new RuntimeMBeanException(e,
- "RuntimeException thrown in postRegister method");
+ "RuntimeException thrown in postRegister method: "+
+ "rethrowing <"+e+">, but keeping the MBean registered");
} catch (Error er) {
+ MBEANSERVER_LOGGER.fine("While registering MBean ["+logicalName+
+ "]: " + "Error thrown by postRegister: " +
+ "rethrowing <"+er+">, but keeping the MBean registered");
throw new RuntimeErrorException(er,
- "Error thrown in postRegister method");
+ "Error thrown in postRegister method: "+
+ "rethrowing <"+er+">, but keeping the MBean registered");
}
}
@@ -1076,15 +1088,28 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
}
- private static void postDeregisterInvoke(MBeanRegistration moi) {
+ private static void postDeregisterInvoke(ObjectName mbean,
+ MBeanRegistration moi) {
try {
moi.postDeregister();
} catch (RuntimeException e) {
+ MBEANSERVER_LOGGER.fine("While unregistering MBean ["+mbean+
+ "]: " + "Exception thrown by postDeregister: " +
+ "rethrowing <"+e+">, although the MBean is succesfully " +
+ "unregistered");
throw new RuntimeMBeanException(e,
- "RuntimeException thrown in postDeregister method");
+ "RuntimeException thrown in postDeregister method: "+
+ "rethrowing <"+e+
+ ">, although the MBean is sucessfully unregistered");
} catch (Error er) {
+ MBEANSERVER_LOGGER.fine("While unregistering MBean ["+mbean+
+ "]: " + "Error thrown by postDeregister: " +
+ "rethrowing <"+er+">, although the MBean is succesfully " +
+ "unregistered");
throw new RuntimeErrorException(er,
- "Error thrown in postDeregister method");
+ "Error thrown in postDeregister method: "+
+ "rethrowing <"+er+
+ ">, although the MBean is sucessfully unregistered");
}
}
diff --git a/jdk/src/share/classes/javax/management/MBeanRegistration.java b/jdk/src/share/classes/javax/management/MBeanRegistration.java
index 1ba1c0d827f..be51f4a894e 100644
--- a/jdk/src/share/classes/javax/management/MBeanRegistration.java
+++ b/jdk/src/share/classes/javax/management/MBeanRegistration.java
@@ -158,7 +158,19 @@ public interface MBeanRegistration {
/**
* Allows the MBean to perform any operations needed after having been
* registered in the MBean server or after the registration has failed.
- *
+ * If the implementation of this method throws a {@link RuntimeException}
+ * or an {@link Error}, the MBean Server will rethrow those inside
+ * a {@link RuntimeMBeanException} or {@link RuntimeErrorException},
+ * respectively. However, throwing an exception in {@code postRegister}
+ * will not change the state of the MBean:
+ * if the MBean was already registered ({@code registrationDone} is
+ * {@code true}), the MBean will remain registered.
+ * This might be confusing for the code calling {@code createMBean()}
+ * or {@code registerMBean()}, as such code might assume that MBean
+ * registration has failed when such an exception is raised.
+ * Therefore it is recommended that implementations of
+ * {@code postRegister} do not throw Runtime Exceptions or Errors if it
+ * can be avoided.
* @param registrationDone Indicates whether or not the MBean has
* been successfully registered in the MBean server. The value
* false means that the registration phase has failed.
@@ -178,6 +190,17 @@ public interface MBeanRegistration {
/**
* Allows the MBean to perform any operations needed after having been
* unregistered in the MBean server.
+ * If the implementation of this method throws a {@link RuntimeException}
+ * or an {@link Error}, the MBean Server will rethrow those inside
+ * a {@link RuntimeMBeanException} or {@link RuntimeErrorException},
+ * respectively. However, throwing an excepption in {@code postDeregister}
+ * will not change the state of the MBean:
+ * the MBean was already successfully deregistered and will remain so.
+ * This might be confusing for the code calling
+ * {@code unregisterMBean()}, as it might assume that MBean deregistration
+ * has failed. Therefore it is recommended that implementations of
+ * {@code postDeregister} do not throw Runtime Exceptions or Errors if it
+ * can be avoided.
*/
public void postDeregister();
diff --git a/jdk/src/share/classes/javax/management/MBeanServer.java b/jdk/src/share/classes/javax/management/MBeanServer.java
index f0d4d16c46f..55a096aefa2 100644
--- a/jdk/src/share/classes/javax/management/MBeanServer.java
+++ b/jdk/src/share/classes/javax/management/MBeanServer.java
@@ -328,11 +328,30 @@ public interface MBeanServer extends MBeanServerConnection {
* preRegister
(MBeanRegistration
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
+ * @exception RuntimeMBeanException If the postRegister
+ * (MBeanRegistration
interface) method of the MBean throws a
+ * RuntimeException
, the registerMBean method will
+ * throw a RuntimeMBeanException
, although the MBean
+ * registration succeeded. In such a case, the MBean will be actually
+ * registered even though the registerMBean method
+ * threw an exception. Note that RuntimeMBeanException
can
+ * also be thrown by preRegister
, in which case the MBean
+ * will not be registered.
+ * @exception RuntimeErrorException If the postRegister
+ * (MBeanRegistration
interface) method of the MBean throws an
+ * Error
, the registerMBean method will
+ * throw a RuntimeErrorException
, although the MBean
+ * registration succeeded. In such a case, the MBean will be actually
+ * registered even though the registerMBean method
+ * threw an exception. Note that RuntimeErrorException
can
+ * also be thrown by preRegister
, in which case the MBean
+ * will not be registered.
* @exception NotCompliantMBeanException This object is not a JMX
* compliant MBean
* @exception RuntimeOperationsException Wraps a
* java.lang.IllegalArgumentException
: The object
* passed in parameter is null or no object name is specified.
+ * @see javax.management.MBeanRegistration
*/
public ObjectInstance registerMBean(Object object, ObjectName name)
throws InstanceAlreadyExistsException, MBeanRegistrationException,
diff --git a/jdk/src/share/classes/javax/management/MBeanServerConnection.java b/jdk/src/share/classes/javax/management/MBeanServerConnection.java
index 4047373c261..3f3bc4442c6 100644
--- a/jdk/src/share/classes/javax/management/MBeanServerConnection.java
+++ b/jdk/src/share/classes/javax/management/MBeanServerConnection.java
@@ -75,6 +75,24 @@ public interface MBeanServerConnection {
* preRegister
(MBeanRegistration
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
+ * @exception RuntimeMBeanException If the postRegister
+ * (MBeanRegistration
interface) method of the MBean throws a
+ * RuntimeException
, the createMBean method will
+ * throw a RuntimeMBeanException
, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the createMBean method
+ * threw an exception. Note that RuntimeMBeanException
can
+ * also be thrown by preRegister
, in which case the MBean
+ * will not be registered.
+ * @exception RuntimeErrorException If the postRegister
+ * (MBeanRegistration
interface) method of the MBean throws an
+ * Error
, the createMBean method will
+ * throw a RuntimeErrorException
, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the createMBean method
+ * threw an exception. Note that RuntimeErrorException
can
+ * also be thrown by preRegister
, in which case the MBean
+ * will not be registered.
* @exception MBeanException The constructor of the MBean has
* thrown an exception
* @exception NotCompliantMBeanException This class is not a JMX
@@ -86,7 +104,7 @@ public interface MBeanServerConnection {
* is specified for the MBean.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
- *
+ * @see javax.management.MBeanRegistration
*/
public ObjectInstance createMBean(String className, ObjectName name)
throws ReflectionException, InstanceAlreadyExistsException,
@@ -129,6 +147,24 @@ public interface MBeanServerConnection {
* preRegister
(MBeanRegistration
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
+ * @exception RuntimeMBeanException If the postRegister
+ * (MBeanRegistration
interface) method of the MBean throws a
+ * RuntimeException
, the createMBean method will
+ * throw a RuntimeMBeanException
, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the createMBean method
+ * threw an exception. Note that RuntimeMBeanException
can
+ * also be thrown by preRegister
, in which case the MBean
+ * will not be registered.
+ * @exception RuntimeErrorException If the postRegister
+ * (MBeanRegistration
interface) method of the MBean throws an
+ * Error
, the createMBean method will
+ * throw a RuntimeErrorException
, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the createMBean method
+ * threw an exception. Note that RuntimeErrorException
can
+ * also be thrown by preRegister
, in which case the MBean
+ * will not be registered.
* @exception MBeanException The constructor of the MBean has
* thrown an exception
* @exception NotCompliantMBeanException This class is not a JMX
@@ -142,6 +178,7 @@ public interface MBeanServerConnection {
* is specified for the MBean.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
+ * @see javax.management.MBeanRegistration
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName)
@@ -185,6 +222,24 @@ public interface MBeanServerConnection {
* preRegister
(MBeanRegistration
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
+ * @exception RuntimeMBeanException If the postRegister
+ * (MBeanRegistration
interface) method of the MBean throws a
+ * RuntimeException
, the createMBean method will
+ * throw a RuntimeMBeanException
, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the createMBean method
+ * threw an exception. Note that RuntimeMBeanException
can
+ * also be thrown by preRegister
, in which case the MBean
+ * will not be registered.
+ * @exception RuntimeErrorException If the postRegister
+ * (MBeanRegistration
interface) method of the MBean throws an
+ * Error
, the createMBean method will
+ * throw a RuntimeErrorException
, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the createMBean method
+ * threw an exception. Note that RuntimeErrorException
can
+ * also be thrown by preRegister
, in which case the MBean
+ * will not be registered.
* @exception MBeanException The constructor of the MBean has
* thrown an exception
* @exception NotCompliantMBeanException This class is not a JMX
@@ -196,7 +251,7 @@ public interface MBeanServerConnection {
* is specified for the MBean.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
- *
+ * @see javax.management.MBeanRegistration
*/
public ObjectInstance createMBean(String className, ObjectName name,
Object params[], String signature[])
@@ -239,6 +294,24 @@ public interface MBeanServerConnection {
* preRegister
(MBeanRegistration
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
+ * @exception RuntimeMBeanException If the postRegister
+ * (MBeanRegistration
interface) method of the MBean throws a
+ * RuntimeException
, the createMBean method will
+ * throw a RuntimeMBeanException
, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the createMBean method
+ * threw an exception. Note that RuntimeMBeanException
can
+ * also be thrown by preRegister
, in which case the MBean
+ * will not be registered.
+ * @exception RuntimeErrorException If the postRegister
method
+ * (MBeanRegistration
interface) method of the MBean throws an
+ * Error
, the createMBean method will
+ * throw a RuntimeErrorException
, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the createMBean method
+ * threw an exception. Note that RuntimeErrorException
can
+ * also be thrown by preRegister
, in which case the MBean
+ * will not be registered.
* @exception MBeanException The constructor of the MBean has
* thrown an exception
* @exception NotCompliantMBeanException This class is not a JMX
@@ -252,7 +325,7 @@ public interface MBeanServerConnection {
* is specified for the MBean.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
- *
+ * @see javax.management.MBeanRegistration
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName, Object params[],
@@ -275,6 +348,24 @@ public interface MBeanServerConnection {
* @exception MBeanRegistrationException The preDeregister
* ((MBeanRegistration
interface) method of the MBean
* has thrown an exception.
+ * @exception RuntimeMBeanException If the postDeregister
+ * (MBeanRegistration
interface) method of the MBean throws a
+ * RuntimeException
, the unregisterMBean method
+ * will throw a RuntimeMBeanException
, although the MBean
+ * unregistration succeeded. In such a case, the MBean will be actually
+ * unregistered even though the unregisterMBean method
+ * threw an exception. Note that RuntimeMBeanException
can
+ * also be thrown by preDeregister
, in which case the MBean
+ * will remain registered.
+ * @exception RuntimeErrorException If the postDeregister
+ * (MBeanRegistration
interface) method of the MBean throws an
+ * Error
, the unregisterMBean method will
+ * throw a RuntimeErrorException
, although the MBean
+ * unregistration succeeded. In such a case, the MBean will be actually
+ * unregistered even though the unregisterMBean method
+ * threw an exception. Note that RuntimeMBeanException
can
+ * also be thrown by preDeregister
, in which case the MBean
+ * will remain registered.
* @exception RuntimeOperationsException Wraps a
* java.lang.IllegalArgumentException
: The object
* name in parameter is null or the MBean you are when trying to
@@ -282,7 +373,7 @@ public interface MBeanServerConnection {
* MBeanServerDelegate} MBean.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
- *
+ * @see javax.management.MBeanRegistration
*/
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException, MBeanRegistrationException,
diff --git a/jdk/test/javax/management/MBeanServer/PostExceptionTest.java b/jdk/test/javax/management/MBeanServer/PostExceptionTest.java
new file mode 100644
index 00000000000..d0e0aa3b829
--- /dev/null
+++ b/jdk/test/javax/management/MBeanServer/PostExceptionTest.java
@@ -0,0 +1,516 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6730926
+ * @summary Check behaviour of MBeanServer when postRegister and postDeregister
+ * throw exceptions.
+ * @author Daniel Fuchs
+ * @compile PostExceptionTest.java
+ * @run main PostExceptionTest
+ */
+
+import javax.management.*;
+import java.io.Serializable;
+import java.net.URL;
+import java.util.EnumSet;
+import javax.management.loading.MLet;
+
+public class PostExceptionTest {
+
+ /**
+ * A test case where we instantiate an ExceptionalWombatMBean (or a
+ * subclass of it) which will throw the exception {@code t} from within
+ * the methods indicated by {@code where}
+ */
+ public static class Case {
+ public final Throwable t;
+ public final EnumSet where;
+ public Case(Throwable t,EnumSet where) {
+ this.t=t; this.where=where;
+ }
+ }
+
+ // Various methods to create an instance of Case in a single line
+ // --------------------------------------------------------------
+
+ public static Case caze(Throwable t, WHERE w) {
+ return new Case(t,EnumSet.of(w));
+ }
+ public static Case caze(Throwable t, EnumSet where) {
+ return new Case(t,where);
+ }
+ public static Case caze(Throwable t, WHERE w, WHERE... rest) {
+ return new Case(t,EnumSet.of(w,rest));
+ }
+
+ /**
+ * Here is the list of our test cases:
+ */
+ public static Case[] cases ={
+ caze(new RuntimeException(),WHERE.PREREGISTER),
+ caze(new RuntimeException(),WHERE.POSTREGISTER),
+ caze(new RuntimeException(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
+ caze(new RuntimeException(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
+ caze(new Exception(),WHERE.PREREGISTER),
+ caze(new Exception(),WHERE.POSTREGISTER),
+ caze(new Exception(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
+ caze(new Exception(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
+ caze(new Error(),WHERE.PREREGISTER),
+ caze(new Error(),WHERE.POSTREGISTER),
+ caze(new Error(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
+ caze(new Error(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
+ caze(new RuntimeException(),EnumSet.allOf(WHERE.class)),
+ caze(new Exception(),EnumSet.allOf(WHERE.class)),
+ caze(new Error(),EnumSet.allOf(WHERE.class)),
+ };
+
+ public static void main(String[] args) throws Exception {
+ System.out.println("Test behaviour of MBeanServer when postRegister " +
+ "or postDeregister throw exceptions");
+ MBeanServer mbs = MBeanServerFactory.newMBeanServer();
+ int failures = 0;
+ final ObjectName n = new ObjectName("test:type=Wombat");
+
+ // We're going to test each cases, using each of the 4 createMBean
+ // forms + registerMBean in turn to create the MBean.
+ // Wich method is used to create the MBean is indicated by "how"
+ //
+ for (Case caze:cases) {
+ for (CREATE how : CREATE.values()) {
+ failures+=test(mbs,n,how,caze.t,caze.where);
+ }
+ }
+ if (failures == 0)
+ System.out.println("Test passed");
+ else {
+ System.out.println("TEST FAILED: " + failures + " failure(s)");
+ System.exit(1);
+ }
+ }
+
+ // Execute a test case composed of:
+ // mbs: The MBeanServer where the MBean will be registered,
+ // name: The name of that MBean
+ // how: How will the MBean be created/registered (which MBeanServer
+ // method)
+ // t: The exception/error that the MBean will throw
+ // where: In which pre/post register/deregister method the exception/error
+ // will be thrown
+ //
+ private static int test(MBeanServer mbs, ObjectName name, CREATE how,
+ Throwable t, EnumSet where)
+ throws Exception {
+ System.out.println("-------<"+how+"> / <"+t+"> / "+ where + "-------");
+
+ int failures = 0;
+ ObjectInstance oi = null;
+ Exception reg = null; // exception thrown by create/register
+ Exception unreg = null; // exception thrown by unregister
+ try {
+ // Create the MBean
+ oi = how.create(t, where, mbs, name);
+ } catch (Exception xx) {
+ reg=xx;
+ }
+ final ObjectName n = (oi==null)?name:oi.getObjectName();
+ final boolean isRegistered = mbs.isRegistered(n);
+ try {
+ // If the MBean is registered, unregister it
+ if (isRegistered) mbs.unregisterMBean(n);
+ } catch (Exception xxx) {
+ unreg=xxx;
+ }
+ final boolean isUnregistered = !mbs.isRegistered(n);
+ if (!isUnregistered) {
+ // if the MBean is still registered (preDeregister threw an
+ // exception) signify to the MBean that it now should stop
+ // throwing anaything and unregister it.
+ JMX.newMBeanProxy(mbs, n, ExceptionalWombatMBean.class).end();
+ mbs.unregisterMBean(n);
+ }
+
+ // Now analyze the result. If we didn't ask the MBean to throw any
+ // exception then reg should be null.
+ if (where.isEmpty() && reg!=null) {
+ System.out.println("Unexpected registration exception: "+
+ reg);
+ throw new RuntimeException("Unexpected registration exception: "+
+ reg,reg);
+ }
+
+ // If we didn't ask the MBean to throw any exception then unreg should
+ // also be null.
+ if (where.isEmpty() && unreg!=null) {
+ System.out.println("Unexpected unregistration exception: "+
+ unreg);
+ throw new RuntimeException("Unexpected unregistration exception: "+
+ unreg,unreg);
+ }
+
+ // If we asked the MBean to throw an exception in either of preRegister
+ // or postRegister, then reg should not be null.
+ if ((where.contains(WHERE.PREREGISTER)
+ || where.contains(WHERE.POSTREGISTER))&& reg==null) {
+ System.out.println("Expected registration exception not " +
+ "thrown by "+where);
+ throw new RuntimeException("Expected registration exception not " +
+ "thrown by "+where);
+ }
+
+ // If we asked the MBean not to throw any exception in preRegister
+ // then the MBean should have been registered, unregisterMBean should
+ // have been called.
+ // If we asked the MBean to throw an exception in either of preDeregister
+ // or postDeregister, then unreg should not be null.
+ if ((where.contains(WHERE.PREDEREGISTER)
+ || where.contains(WHERE.POSTDEREGISTER))&& unreg==null
+ && !where.contains(WHERE.PREREGISTER)) {
+ System.out.println("Expected unregistration exception not " +
+ "thrown by "+where);
+ throw new RuntimeException("Expected unregistration exception not " +
+ "thrown by "+where);
+ }
+
+ // If we asked the MBean to throw an exception in preRegister
+ // then the MBean should not have been registered.
+ if (where.contains(WHERE.PREREGISTER)) {
+ if (isRegistered) {
+ System.out.println("MBean is still registered [" +
+ where+
+ "]: "+name+" / "+reg);
+ throw new RuntimeException("MBean is still registered [" +
+ where+
+ "]: "+name+" / "+reg,reg);
+ }
+ }
+
+ // If we asked the MBean not to throw an exception in preRegister,
+ // but to throw an exception in postRegister, then the MBean should
+ // have been registered.
+ if (where.contains(WHERE.POSTREGISTER) &&
+ !where.contains(WHERE.PREREGISTER)) {
+ if (!isRegistered) {
+ System.out.println("MBean is already unregistered [" +
+ where+
+ "]: "+name+" / "+reg);
+ throw new RuntimeException("MBean is already unregistered [" +
+ where+
+ "]: "+name+" / "+reg,reg);
+ }
+ }
+
+ // If we asked the MBean to throw an exception in preRegister,
+ // check that the exception we caught was as expected.
+ //
+ if (where.contains(WHERE.PREREGISTER)) {
+ WHERE.PREREGISTER.check(reg, t);
+ } else if (where.contains(WHERE.POSTREGISTER)) {
+ // If we asked the MBean to throw an exception in postRegister,
+ // check that the exception we caught was as expected.
+ // We don't do this check if we asked the MBean to also throw an
+ // exception in pre register, because postRegister will not have
+ // been called.
+ WHERE.POSTREGISTER.check(reg, t);
+ }
+
+ if (!isRegistered) return failures;
+
+ // The MBean was registered, so unregisterMBean was called. Check
+ // unregisterMBean exceptions...
+ //
+
+ // If we asked the MBean to throw an exception in preDeregister
+ // then the MBean should not have been deregistered.
+ if (where.contains(WHERE.PREDEREGISTER)) {
+ if (isUnregistered) {
+ System.out.println("MBean is already unregistered [" +
+ where+
+ "]: "+name+" / "+unreg);
+ throw new RuntimeException("MBean is already unregistered [" +
+ where+
+ "]: "+name+" / "+unreg,unreg);
+ }
+ }
+
+ // If we asked the MBean not to throw an exception in preDeregister,
+ // but to throw an exception in postDeregister, then the MBean should
+ // have been deregistered.
+ if (where.contains(WHERE.POSTDEREGISTER) &&
+ !where.contains(WHERE.PREDEREGISTER)) {
+ if (!isUnregistered) {
+ System.out.println("MBean is not unregistered [" +
+ where+
+ "]: "+name+" / "+unreg);
+ throw new RuntimeException("MBean is not unregistered [" +
+ where+
+ "]: "+name+" / "+unreg,unreg);
+ }
+ }
+
+ // If we asked the MBean to throw an exception in preDeregister,
+ // check that the exception we caught was as expected.
+ //
+ if (where.contains(WHERE.PREDEREGISTER)) {
+ WHERE.PREDEREGISTER.check(unreg, t);
+ } else if (where.contains(WHERE.POSTDEREGISTER)) {
+ // If we asked the MBean to throw an exception in postDeregister,
+ // check that the exception we caught was as expected.
+ // We don't do this check if we asked the MBean to also throw an
+ // exception in pre register, because postRegister will not have
+ // been called.
+ WHERE.POSTDEREGISTER.check(unreg, t);
+ }
+ return failures;
+ }
+
+ /**
+ * This enum lists the 4 methods in MBeanRegistration.
+ */
+ public static enum WHERE {
+
+ PREREGISTER, POSTREGISTER, PREDEREGISTER, POSTDEREGISTER;
+
+ // Checks that an exception thrown by the MBeanServer correspond to
+ // what is expected when an MBean throws an exception in this
+ // MBeanRegistration method ("this" is one of the 4 enum values above)
+ //
+ public void check(Exception thrown, Throwable t)
+ throws Exception {
+ if (t instanceof RuntimeException) {
+ if (!(thrown instanceof RuntimeMBeanException)) {
+ System.out.println("Expected RuntimeMBeanException, got "+
+ thrown);
+ throw new Exception("Expected RuntimeMBeanException, got "+
+ thrown);
+ }
+ } else if (t instanceof Error) {
+ if (!(thrown instanceof RuntimeErrorException)) {
+ System.out.println("Expected RuntimeErrorException, got "+
+ thrown);
+ throw new Exception("Expected RuntimeErrorException, got "+
+ thrown);
+ }
+ } else if (t instanceof Exception) {
+ if (EnumSet.of(POSTDEREGISTER,POSTREGISTER).contains(this)) {
+ if (!(thrown instanceof RuntimeMBeanException)) {
+ System.out.println("Expected RuntimeMBeanException, got "+
+ thrown);
+ throw new Exception("Expected RuntimeMBeanException, got "+
+ thrown);
+ }
+ if (! (thrown.getCause() instanceof RuntimeException)) {
+ System.out.println("Bad cause: " +
+ "expected RuntimeException, " +
+ "got <"+thrown.getCause()+">");
+ throw new Exception("Bad cause: " +
+ "expected RuntimeException, " +
+ "got <"+thrown.getCause()+">");
+ }
+ }
+ if (EnumSet.of(PREDEREGISTER,PREREGISTER).contains(this)) {
+ if (!(thrown instanceof MBeanRegistrationException)) {
+ System.out.println("Expected " +
+ "MBeanRegistrationException, got "+
+ thrown);
+ throw new Exception("Expected " +
+ "MBeanRegistrationException, got "+
+ thrown);
+ }
+ if (! (thrown.getCause() instanceof Exception)) {
+ System.out.println("Bad cause: " +
+ "expected Exception, " +
+ "got <"+thrown.getCause()+">");
+ throw new Exception("Bad cause: " +
+ "expected Exception, " +
+ "got <"+thrown.getCause()+">");
+ }
+ }
+ }
+
+ }
+ }
+
+ /**
+ * This enum lists the 5 methods to create and register an
+ * ExceptionalWombat MBean
+ */
+ public static enum CREATE {
+
+ CREATE1() {
+ // Creates an ExceptionalWombat MBean using createMBean form #1
+ public ObjectInstance create(Throwable t, EnumSet where,
+ MBeanServer server, ObjectName name) throws Exception {
+ ExceptionallyHackyWombat.t = t;
+ ExceptionallyHackyWombat.w = where;
+ return server.createMBean(
+ ExceptionallyHackyWombat.class.getName(),
+ name);
+ }
+ },
+ CREATE2() {
+ // Creates an ExceptionalWombat MBean using createMBean form #2
+ public ObjectInstance create(Throwable t, EnumSet where,
+ MBeanServer server, ObjectName name) throws Exception {
+ ExceptionallyHackyWombat.t = t;
+ ExceptionallyHackyWombat.w = where;
+ final ObjectName loaderName = registerMLet(server);
+ return server.createMBean(
+ ExceptionallyHackyWombat.class.getName(),
+ name, loaderName);
+ }
+ },
+ CREATE3() {
+ // Creates an ExceptionalWombat MBean using createMBean form #3
+ public ObjectInstance create(Throwable t, EnumSet where,
+ MBeanServer server, ObjectName name) throws Exception {
+ final Object[] params = {t, where};
+ final String[] signature = {Throwable.class.getName(),
+ EnumSet.class.getName()
+ };
+ return server.createMBean(
+ ExceptionalWombat.class.getName(), name,
+ params, signature);
+ }
+ },
+ CREATE4() {
+ // Creates an ExceptionalWombat MBean using createMBean form #4
+ public ObjectInstance create(Throwable t, EnumSet where,
+ MBeanServer server, ObjectName name) throws Exception {
+ final Object[] params = {t, where};
+ final String[] signature = {Throwable.class.getName(),
+ EnumSet.class.getName()
+ };
+ return server.createMBean(
+ ExceptionalWombat.class.getName(), name,
+ registerMLet(server), params, signature);
+ }
+ },
+ REGISTER() {
+ // Creates an ExceptionalWombat MBean using registerMBean
+ public ObjectInstance create(Throwable t, EnumSet where,
+ MBeanServer server, ObjectName name) throws Exception {
+ final ExceptionalWombat wombat =
+ new ExceptionalWombat(t, where);
+ return server.registerMBean(wombat, name);
+ }
+ };
+
+ // Creates an ExceptionalWombat MBean using the method denoted by this
+ // Enum value - one of CREATE1, CREATE2, CREATE3, CREATE4, or REGISTER.
+ public abstract ObjectInstance create(Throwable t, EnumSet where,
+ MBeanServer server, ObjectName name) throws Exception;
+
+ // This is a bit of a hack - we use an MLet that delegates to the
+ // System ClassLoader so that we can use createMBean form #2 and #3
+ // while still using the same class loader (system).
+ // This is necessary to make the ExceptionallyHackyWombatMBean work ;-)
+ //
+ public ObjectName registerMLet(MBeanServer server) throws Exception {
+ final ObjectName name = new ObjectName("test:type=MLet");
+ if (server.isRegistered(name)) {
+ return name;
+ }
+ final MLet mlet = new MLet(new URL[0],
+ ClassLoader.getSystemClassLoader());
+ return server.registerMBean(mlet, name).getObjectName();
+ }
+ }
+
+ /**
+ *A Wombat MBean that can throw exceptions or errors in any of the
+ * MBeanRegistration methods.
+ */
+ public static interface ExceptionalWombatMBean {
+ // Tells the MBean to stop throwing exceptions - we sometime
+ // need to call this at the end of the test so that we can
+ // actually unregister the MBean.
+ public void end();
+ }
+
+ /**
+ *A Wombat MBean that can throw exceptions or errors in any of the
+ * MBeanRegistration methods.
+ */
+ public static class ExceptionalWombat
+ implements ExceptionalWombatMBean, MBeanRegistration {
+
+ private final Throwable throwable;
+ private final EnumSet where;
+ private volatile boolean end=false;
+
+ public ExceptionalWombat(Throwable t, EnumSet where) {
+ this.throwable=t; this.where=where;
+ }
+ private Exception doThrow() {
+ if (throwable instanceof Error)
+ throw (Error)throwable;
+ if (throwable instanceof RuntimeException)
+ throw (RuntimeException)throwable;
+ return (Exception)throwable;
+ }
+ public ObjectName preRegister(MBeanServer server, ObjectName name)
+ throws Exception {
+ if (!end && where.contains(WHERE.PREREGISTER))
+ throw doThrow();
+ return name;
+ }
+
+ public void postRegister(Boolean registrationDone) {
+ if (!end && where.contains(WHERE.POSTREGISTER))
+ throw new RuntimeException(doThrow());
+ }
+
+ public void preDeregister() throws Exception {
+ if (!end && where.contains(WHERE.PREDEREGISTER))
+ throw doThrow();
+ }
+
+ public void postDeregister() {
+ if (!end && where.contains(WHERE.POSTREGISTER))
+ throw new RuntimeException(doThrow());
+ }
+
+ public void end() {
+ this.end=true;
+ }
+ }
+
+ /**
+ * This is a big ugly hack to call createMBean form #1 and #2 - where
+ * the empty constructor is used. Since we still want to supply parameters
+ * to the ExceptionalWombat super class, we temporarily store these
+ * parameter value in a static volatile before calling create MBean.
+ * Of course this only works because our test is sequential and single
+ * threaded, and nobody but our test uses this ExceptionallyHackyWombat.
+ */
+ public static class ExceptionallyHackyWombat extends ExceptionalWombat {
+ public static volatile Throwable t;
+ public static volatile EnumSet w;
+ public ExceptionallyHackyWombat() {
+ super(t,w);
+ }
+ }
+
+}
From 22260fb95dbe1499afb960cb3a9eec0f6c5a2382 Mon Sep 17 00:00:00 2001
From: Daniel Fuchs
Date: Thu, 31 Jul 2008 14:20:11 +0200
Subject: [PATCH 02/13] 6689505: Improve MBeanServerNotification.toString
Reviewed-by: emcmanus
---
.../management/MBeanServerNotification.java | 98 +++++++------
.../MBeanServerNotificationTest.java | 132 ++++++++++++++++++
2 files changed, 185 insertions(+), 45 deletions(-)
create mode 100644 jdk/test/javax/management/MBeanServer/MBeanServerNotificationTest.java
diff --git a/jdk/src/share/classes/javax/management/MBeanServerNotification.java b/jdk/src/share/classes/javax/management/MBeanServerNotification.java
index 25f125df0f7..91362f8e0ed 100644
--- a/jdk/src/share/classes/javax/management/MBeanServerNotification.java
+++ b/jdk/src/share/classes/javax/management/MBeanServerNotification.java
@@ -38,56 +38,64 @@ package javax.management;
*
* @since 1.5
*/
- public class MBeanServerNotification extends Notification {
+public class MBeanServerNotification extends Notification {
- /* Serial version */
- private static final long serialVersionUID = 2876477500475969677L;
+ /* Serial version */
+ private static final long serialVersionUID = 2876477500475969677L;
+ /**
+ * Notification type denoting that an MBean has been registered.
+ * Value is "JMX.mbean.registered".
+ */
+ public static final String REGISTRATION_NOTIFICATION =
+ "JMX.mbean.registered";
+ /**
+ * Notification type denoting that an MBean has been unregistered.
+ * Value is "JMX.mbean.unregistered".
+ */
+ public static final String UNREGISTRATION_NOTIFICATION =
+ "JMX.mbean.unregistered";
+ /**
+ * @serial The object names of the MBeans concerned by this notification
+ */
+ private final ObjectName objectName;
- /**
- * Notification type denoting that an MBean has been registered. Value is "JMX.mbean.registered".
- */
- public static final String REGISTRATION_NOTIFICATION = "JMX.mbean.registered" ;
+ /**
+ * Creates an MBeanServerNotification object specifying object names of
+ * the MBeans that caused the notification and the specified notification
+ * type.
+ *
+ * @param type A string denoting the type of the
+ * notification. Set it to one these values: {@link
+ * #REGISTRATION_NOTIFICATION}, {@link
+ * #UNREGISTRATION_NOTIFICATION}.
+ * @param source The MBeanServerNotification object responsible
+ * for forwarding MBean server notification.
+ * @param sequenceNumber A sequence number that can be used to order
+ * received notifications.
+ * @param objectName The object name of the MBean that caused the
+ * notification.
+ *
+ */
+ public MBeanServerNotification(String type, Object source,
+ long sequenceNumber, ObjectName objectName) {
+ super(type, source, sequenceNumber);
+ this.objectName = objectName;
+ }
- /**
- * Notification type denoting that an MBean has been unregistered. Value is "JMX.mbean.unregistered".
- */
- public static final String UNREGISTRATION_NOTIFICATION = "JMX.mbean.unregistered" ;
+ /**
+ * Returns the object name of the MBean that caused the notification.
+ *
+ * @return the object name of the MBean that caused the notification.
+ */
+ public ObjectName getMBeanName() {
+ return objectName;
+ }
+ @Override
+ public String toString() {
+ return super.toString() + "[mbeanName=" + objectName + "]";
- /**
- * @serial The object names of the MBeans concerned by this notification
- */
- private final ObjectName objectName;
-
-
- /**
- * Creates an MBeanServerNotification object specifying object names of
- * the MBeans that caused the notification and the specified notification type.
- *
- * @param type A string denoting the type of the
- * notification. Set it to one these values: {@link
- * #REGISTRATION_NOTIFICATION}, {@link
- * #UNREGISTRATION_NOTIFICATION}.
- * @param source The MBeanServerNotification object responsible
- * for forwarding MBean server notification.
- * @param sequenceNumber A sequence number that can be used to order
- * received notifications.
- * @param objectName The object name of the MBean that caused the notification.
- *
- */
- public MBeanServerNotification(String type, Object source, long sequenceNumber, ObjectName objectName ) {
- super (type,source,sequenceNumber) ;
- this.objectName = objectName ;
- }
-
- /**
- * Returns the object name of the MBean that caused the notification.
- *
- * @return the object name of the MBean that caused the notification.
- */
- public ObjectName getMBeanName() {
- return objectName ;
- }
+ }
}
diff --git a/jdk/test/javax/management/MBeanServer/MBeanServerNotificationTest.java b/jdk/test/javax/management/MBeanServer/MBeanServerNotificationTest.java
new file mode 100644
index 00000000000..54fd6ba2d86
--- /dev/null
+++ b/jdk/test/javax/management/MBeanServer/MBeanServerNotificationTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6689505
+ * @summary Checks that MBeanServerNotification.toString contains the
+ * MBean name.
+ * @author Daniel Fuchs
+ * @compile MBeanServerNotificationTest.java
+ * @run main MBeanServerNotificationTest
+ */
+
+import com.sun.jmx.mbeanserver.Util;
+import javax.management.*;
+import java.util.concurrent.*;
+
+public class MBeanServerNotificationTest {
+ final static String[] names = {
+ ":type=Wombat", "wombat:type=Wombat",null,
+ };
+ public static void main(String[] args) throws Exception {
+ System.out.println("Test that MBeanServerNotification.toString " +
+ "contains the name of the MBean being registered " +
+ "or unregistered.");
+ int failures = 0;
+ final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
+ for (String str:names) {
+ try {
+ final ObjectName name = (str==null)?null:new ObjectName(str);
+ failures+=test(mbs, name, name!=null);
+ } catch(Exception x) {
+ x.printStackTrace(System.out);
+ System.out.println("Test failed for: "+str);
+ failures++;
+ }
+ }
+ if (failures == 0)
+ System.out.println("Test passed");
+ else {
+ System.out.println("TEST FAILED: " + failures + " failure(s)");
+ System.exit(1);
+ }
+ }
+
+ private static enum Registration {
+ REGISTER(MBeanServerNotification.REGISTRATION_NOTIFICATION),
+ UNREGISTER(MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
+ final String type;
+ private Registration(String type) {this.type = type;}
+ public int test(MBeanServerNotification n, ObjectName name) {
+ int failures = 0;
+ System.out.println("Testing: "+n);
+ if (!n.toString().endsWith("[type="+type+
+ "][message="+n.getMessage()+
+ "][mbeanName="+name+"]")) {
+ System.err.println("Test failed for "+ type+
+ " ["+name+"]: "+n);
+ failures++;
+ }
+ return failures;
+ }
+ public MBeanServerNotification create(ObjectName name) {
+ return new MBeanServerNotification(type,
+ MBeanServerDelegate.DELEGATE_NAME, next(), name);
+ }
+ private static long next = 0;
+ private static synchronized long next() {return next++;}
+
+ }
+
+ private static int test(MBeanServer mbs, ObjectName name,
+ boolean register)
+ throws Exception {
+ System.out.println("--------" + name + "--------");
+
+ int failures = 0;
+ for (Registration reg : Registration.values()) {
+ failures = reg.test(reg.create(name), name);
+ }
+ if (!register) return failures;
+
+ final ArrayBlockingQueue queue =
+ new ArrayBlockingQueue(10);
+ final NotificationListener listener = new NotificationListener() {
+ public void handleNotification(Notification notification,
+ Object handback) {
+ try {
+ queue.put(notification);
+ } catch(Exception x) {
+ x.printStackTrace(System.out);
+ }
+ }
+ };
+ mbs.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME,
+ listener, null, name);
+ final ObjectInstance oi = mbs.registerMBean(new Wombat(), name);
+ try {
+ failures+=Registration.REGISTER.test((MBeanServerNotification)
+ queue.poll(2, TimeUnit.SECONDS), oi.getObjectName());
+ } finally {
+ mbs.unregisterMBean(oi.getObjectName());
+ failures+=Registration.UNREGISTER.test((MBeanServerNotification)
+ queue.poll(2, TimeUnit.SECONDS), oi.getObjectName());
+ }
+ return failures;
+ }
+
+ public static interface WombatMBean {}
+ public static class Wombat implements WombatMBean {}
+
+}
From cf105cf085769760af94f8c2d350382f99b68a38 Mon Sep 17 00:00:00 2001
From: Shanliang Jiang
Date: Thu, 31 Jul 2008 15:31:13 +0200
Subject: [PATCH 03/13] 5108776: Add reliable event handling to the JMX API
6218920: API bug - impossible to delete last MBeanServerForwarder on a
connector
Reviewed-by: emcmanus
---
.../sun/jmx/event/DaemonThreadFactory.java | 77 +
.../com/sun/jmx/event/EventBuffer.java | 252 +++
.../com/sun/jmx/event/EventClientFactory.java | 46 +
.../com/sun/jmx/event/EventConnection.java | 81 +
.../com/sun/jmx/event/EventParams.java | 65 +
.../com/sun/jmx/event/LeaseManager.java | 146 ++
.../com/sun/jmx/event/LeaseRenewer.java | 143 ++
.../com/sun/jmx/event/ReceiverBuffer.java | 97 ++
.../sun/jmx/event/RepeatedSingletonJob.java | 117 ++
.../jmx/interceptor/MBeanServerSupport.java | 1341 ++++++++++++++++
.../jmx/interceptor/SingleMBeanForwarder.java | 414 +++++
.../com/sun/jmx/interceptor/package.html | 3 +
.../sun/jmx/mbeanserver/MBeanInjector.java | 2 +-
.../com/sun/jmx/mbeanserver/MBeanSupport.java | 1 -
.../jmx/mbeanserver/PerThreadGroupPool.java | 71 +
.../classes/com/sun/jmx/mbeanserver/Util.java | 96 ++
.../remote/internal/ClientNotifForwarder.java | 14 +
.../jmx/remote/internal/ProxyInputStream.java | 21 +-
.../com/sun/jmx/remote/internal/ProxyRef.java | 21 +-
.../remote/internal/ServerNotifForwarder.java | 40 +-
.../jmx/remote/security/FileLoginModule.java | 16 +-
.../com/sun/jmx/remote/util/EnvHelp.java | 175 +-
.../remote/util/EventClientConnection.java | 471 ++++++
.../com/sun/jmx/snmp/tasks/ThreadService.java | 10 -
.../javax/management/ImmutableDescriptor.java | 4 +-
.../classes/javax/management/MBeanServer.java | 8 +-
.../management/MBeanServerConnection.java | 86 +-
.../classes/javax/management/MXBean.java | 4 +-
.../classes/javax/management/QueryParser.java | 2 +-
.../javax/management/StringValueExp.java | 1 +
.../javax/management/event/EventClient.java | 1068 +++++++++++++
.../management/event/EventClientDelegate.java | 766 +++++++++
.../event/EventClientDelegateMBean.java | 318 ++++
.../event/EventClientNotFoundException.java | 79 +
.../javax/management/event/EventConsumer.java | 98 ++
.../management/event/EventForwarder.java | 63 +
.../javax/management/event/EventReceiver.java | 77 +
.../javax/management/event/EventRelay.java | 80 +
.../management/event/EventSubscriber.java | 381 +++++
.../event/FetchingEventForwarder.java | 151 ++
.../management/event/FetchingEventRelay.java | 389 +++++
.../javax/management/event/ListenerInfo.java | 169 ++
.../management/event/NotificationManager.java | 136 ++
.../event/RMIPushEventForwarder.java | 198 +++
.../management/event/RMIPushEventRelay.java | 161 ++
.../javax/management/event/RMIPushServer.java | 48 +
.../javax/management/event/package-info.java | 312 ++++
.../javax/management/loading/MLet.java | 36 +-
.../modelmbean/ModelMBeanInfoSupport.java | 4 +-
.../modelmbean/RequiredModelMBean.java | 2 +-
.../management/relation/RelationService.java | 4 +-
.../remote/IdentityMBeanServerForwarder.java | 303 ++++
.../javax/management/remote/JMXConnector.java | 20 +
.../management/remote/JMXConnectorServer.java | 355 ++++-
.../remote/JMXConnectorServerFactory.java | 5 +-
.../remote/JMXConnectorServerMBean.java | 19 +-
.../remote/rmi/RMIConnectionImpl.java | 368 ++++-
.../management/remote/rmi/RMIConnector.java | 115 +-
.../remote/rmi/RMIConnectorServer.java | 44 +-
.../MBeanServer/DynamicWrapperMBeanTest.java | 164 ++
.../MBeanServer/OldMBeanServerTest.java | 1410 +++++++++++++++++
.../eventService/AddRemoveListenerTest.java | 371 +++++
.../eventService/CustomForwarderTest.java | 348 ++++
.../eventService/EventClientExecutorTest.java | 191 +++
.../EventDelegateSecurityTest.java | 289 ++++
.../eventService/EventManagerTest.java | 221 +++
.../management/eventService/FetchingTest.java | 276 ++++
.../LeaseManagerDeadlockTest.java | 96 ++
.../management/eventService/LeaseTest.java | 361 +++++
.../management/eventService/ListenerTest.java | 224 +++
.../MyFetchingEventForwarder.java | 53 +
.../NotSerializableNotifTest.java | 227 +++
.../management/eventService/PublishTest.java | 184 +++
.../ReconnectableConnectorTest.java | 488 ++++++
.../eventService/SharingThreadTest.java | 364 +++++
.../eventService/SubscribeTest.java | 127 ++
.../eventService/UsingEventService.java | 84 +
.../mxbean/GenericArrayTypeTest.java | 56 +-
.../javax/management/mxbean/LeakTest.java | 2 +-
.../mxbean/MBeanOperationInfoTest.java | 3 +-
.../javax/management/mxbean/MXBeanTest.java | 2 +-
.../management/mxbean/ThreadMXBeanTest.java | 3 +-
.../javax/management/mxbean/TigerMXBean.java | 16 +-
.../query/QueryNotifFilterTest.java | 2 +-
.../mandatory/connection/CloseServerTest.java | 77 +-
.../mandatory/connection/DeadLockTest.java | 4 +
.../mandatory/connection/IdleTimeoutTest.java | 10 +-
.../mandatory/connection/RMIExitTest.java | 16 +-
.../mandatory/connection/ReconnectTest.java | 3 +-
.../connectorServer/ForwarderChainTest.java | 274 ++++
.../StandardForwardersTest.java | 177 +++
.../mandatory/loading/MissingClassTest.java | 62 +-
.../remote/mandatory/notif/AddRemoveTest.java | 20 +-
.../remote/mandatory/notif/DiffHBTest.java | 298 ++--
.../notif/EmptyDomainNotificationTest.java | 24 +-
.../mandatory/notif/ListenerScaleTest.java | 41 +-
.../NotifBufferSizePropertyNameTest.java | 5 +-
.../notif/NotifReconnectDeadlockTest.java | 23 +-
.../NotificationAccessControllerTest.java | 3 +-
.../notif/NotificationBufferCreationTest.java | 5 +
.../notif/NotificationBufferDeadlockTest.java | 16 +-
.../notif/NotificationEmissionTest.java | 73 +-
.../remote/mandatory/notif/RMINotifTest.java | 52 +-
.../mandatory/notif/UnexpectedNotifTest.java | 112 +-
104 files changed, 15871 insertions(+), 580 deletions(-)
create mode 100644 jdk/src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/event/EventBuffer.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/event/EventClientFactory.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/event/EventConnection.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/event/EventParams.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/event/LeaseManager.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/event/LeaseRenewer.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/event/ReceiverBuffer.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/mbeanserver/PerThreadGroupPool.java
create mode 100644 jdk/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java
create mode 100644 jdk/src/share/classes/javax/management/event/EventClient.java
create mode 100644 jdk/src/share/classes/javax/management/event/EventClientDelegate.java
create mode 100644 jdk/src/share/classes/javax/management/event/EventClientDelegateMBean.java
create mode 100644 jdk/src/share/classes/javax/management/event/EventClientNotFoundException.java
create mode 100644 jdk/src/share/classes/javax/management/event/EventConsumer.java
create mode 100644 jdk/src/share/classes/javax/management/event/EventForwarder.java
create mode 100644 jdk/src/share/classes/javax/management/event/EventReceiver.java
create mode 100644 jdk/src/share/classes/javax/management/event/EventRelay.java
create mode 100644 jdk/src/share/classes/javax/management/event/EventSubscriber.java
create mode 100644 jdk/src/share/classes/javax/management/event/FetchingEventForwarder.java
create mode 100644 jdk/src/share/classes/javax/management/event/FetchingEventRelay.java
create mode 100644 jdk/src/share/classes/javax/management/event/ListenerInfo.java
create mode 100644 jdk/src/share/classes/javax/management/event/NotificationManager.java
create mode 100644 jdk/src/share/classes/javax/management/event/RMIPushEventForwarder.java
create mode 100644 jdk/src/share/classes/javax/management/event/RMIPushEventRelay.java
create mode 100644 jdk/src/share/classes/javax/management/event/RMIPushServer.java
create mode 100644 jdk/src/share/classes/javax/management/event/package-info.java
create mode 100644 jdk/src/share/classes/javax/management/remote/IdentityMBeanServerForwarder.java
create mode 100644 jdk/test/javax/management/MBeanServer/DynamicWrapperMBeanTest.java
create mode 100644 jdk/test/javax/management/MBeanServer/OldMBeanServerTest.java
create mode 100644 jdk/test/javax/management/eventService/AddRemoveListenerTest.java
create mode 100644 jdk/test/javax/management/eventService/CustomForwarderTest.java
create mode 100644 jdk/test/javax/management/eventService/EventClientExecutorTest.java
create mode 100644 jdk/test/javax/management/eventService/EventDelegateSecurityTest.java
create mode 100644 jdk/test/javax/management/eventService/EventManagerTest.java
create mode 100644 jdk/test/javax/management/eventService/FetchingTest.java
create mode 100644 jdk/test/javax/management/eventService/LeaseManagerDeadlockTest.java
create mode 100644 jdk/test/javax/management/eventService/LeaseTest.java
create mode 100644 jdk/test/javax/management/eventService/ListenerTest.java
create mode 100644 jdk/test/javax/management/eventService/MyFetchingEventForwarder.java
create mode 100644 jdk/test/javax/management/eventService/NotSerializableNotifTest.java
create mode 100644 jdk/test/javax/management/eventService/PublishTest.java
create mode 100644 jdk/test/javax/management/eventService/ReconnectableConnectorTest.java
create mode 100644 jdk/test/javax/management/eventService/SharingThreadTest.java
create mode 100644 jdk/test/javax/management/eventService/SubscribeTest.java
create mode 100644 jdk/test/javax/management/eventService/UsingEventService.java
create mode 100644 jdk/test/javax/management/remote/mandatory/connectorServer/ForwarderChainTest.java
create mode 100644 jdk/test/javax/management/remote/mandatory/connectorServer/StandardForwardersTest.java
diff --git a/jdk/src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java b/jdk/src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java
new file mode 100644
index 00000000000..85377d4d36b
--- /dev/null
+++ b/jdk/src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java
@@ -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");
+}
diff --git a/jdk/src/share/classes/com/sun/jmx/event/EventBuffer.java b/jdk/src/share/classes/com/sun/jmx/event/EventBuffer.java
new file mode 100644
index 00000000000..bfdabdbb457
--- /dev/null
+++ b/jdk/src/share/classes/com/sun/jmx/event/EventBuffer.java
@@ -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());
+ }
+
+ public EventBuffer(int capacity, final List 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 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 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");
+}
diff --git a/jdk/src/share/classes/com/sun/jmx/event/EventClientFactory.java b/jdk/src/share/classes/com/sun/jmx/event/EventClientFactory.java
new file mode 100644
index 00000000000..f08b0990d33
--- /dev/null
+++ b/jdk/src/share/classes/com/sun/jmx/event/EventClientFactory.java
@@ -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();
+
+}
diff --git a/jdk/src/share/classes/com/sun/jmx/event/EventConnection.java b/jdk/src/share/classes/com/sun/jmx/event/EventConnection.java
new file mode 100644
index 00000000000..d52d9d3de51
--- /dev/null
+++ b/jdk/src/share/classes/com/sun/jmx/event/EventConnection.java
@@ -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);
+ }
+ }
+}
diff --git a/jdk/src/share/classes/com/sun/jmx/event/EventParams.java b/jdk/src/share/classes/com/sun/jmx/event/EventParams.java
new file mode 100644
index 00000000000..f941fbe15f3
--- /dev/null
+++ b/jdk/src/share/classes/com/sun/jmx/event/EventParams.java
@@ -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");
+}
diff --git a/jdk/src/share/classes/com/sun/jmx/event/LeaseManager.java b/jdk/src/share/classes/com/sun/jmx/event/LeaseManager.java
new file mode 100644
index 00000000000..cb1b88bf514
--- /dev/null
+++ b/jdk/src/share/classes/com/sun/jmx/event/LeaseManager.java
@@ -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;
+
+/**
+ * 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.
+ * @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);
+ }
+
+ /**
+ * 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.
+ *
+ * Calling this method after the lease has expired will return zero
+ * immediately and have no other effect.
+ *
+ * @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;
+ }
+
+ /**
+ * Cancel the lease without calling the expiry callback.
+ */
+ 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");
+
+}
diff --git a/jdk/src/share/classes/com/sun/jmx/event/LeaseRenewer.java b/jdk/src/share/classes/com/sun/jmx/event/LeaseRenewer.java
new file mode 100644
index 00000000000..6f2986e5c0b
--- /dev/null
+++ b/jdk/src/share/classes/com/sun/jmx/event/LeaseRenewer.java
@@ -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 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 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");
+}
diff --git a/jdk/src/share/classes/com/sun/jmx/event/ReceiverBuffer.java b/jdk/src/share/classes/com/sun/jmx/event/ReceiverBuffer.java
new file mode 100644
index 00000000000..7c2a737a220
--- /dev/null
+++ b/jdk/src/share/classes/com/sun/jmx/event/ReceiverBuffer.java
@@ -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 notifList
+ = new ArrayList();
+ private long start = 0;
+ private int lost = 0;
+
+ private static final ClassLogger logger =
+ new ClassLogger("javax.management.event", "ReceiverBuffer");
+}
diff --git a/jdk/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java b/jdk/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java
new file mode 100644
index 00000000000..7de1b40e92d
--- /dev/null
+++ b/jdk/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.jmx.event;
+
+import com.sun.jmx.remote.util.ClassLogger;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * 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()}.
+ * @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");
+}
diff --git a/jdk/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java b/jdk/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java
new file mode 100644
index 00000000000..e7144cf36e1
--- /dev/null
+++ b/jdk/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java
@@ -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;
+
+/**
+ * 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.
+ *
+ * There are two main use cases for this class: special-purpose MBeanServer implementations,
+ * and namespaces containing Virtual MBeans. The next
+ * sections explain these use cases.
+ *
+ * In the simplest case, a subclass needs to implement only two methods:
+ *
+ *
+ * -
+ * {@link #getNames getNames} which returns the name of
+ * all MBeans handled by this {@code MBeanServer}.
+ *
+ * -
+ * {@link #getDynamicMBeanFor getDynamicMBeanFor} which returns a
+ * {@link DynamicMBean} that can be used to invoke operations and
+ * obtain meta data (MBeanInfo) on a given MBean.
+ *
+ *
+ *
+ * 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.
+ *
+ * Special-purpose MBeanServer implementations
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Here is an example of a special-purpose {@code MBeanServer}
+ * implementation that contains exactly one MBean, which is specified at the
+ * time of creation.
+ *
+ *
+ * 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;
+ * }
+ *
+ * @Override
+ * protected {@code Set} {@link #getNames getNames}() {
+ * return Collections.singleton(objectName);
+ * }
+ *
+ * @Override
+ * public DynamicMBean {@link #getDynamicMBeanFor
+ * getDynamicMBeanFor}(ObjectName name)
+ * throws InstanceNotFoundException {
+ * if (objectName.equals(name))
+ * return mbean;
+ * else
+ * throw new InstanceNotFoundException(name);
+ * }
+ * }
+ *
+ *
+ * Using this class, you could make an {@code MBeanServer} that contains
+ * a {@link javax.management.timer.Timer Timer} MBean like this:
+ *
+ *
+ * Timer timer = new Timer();
+ * DynamicMBean mbean = new {@link javax.management.StandardMBean
+ * StandardMBean}(timer, TimerMBean.class);
+ * ObjectName name = new ObjectName("com.example:type=Timer");
+ * MBeanServer timerMBS = new SingletonMBeanServer(name, mbean);
+ *
+ *
+ * When {@code getDynamicMBeanFor} always returns the same object for the
+ * same name, as here, notifications work in the expected way: if the object
+ * is a {@link NotificationEmitter} then listeners can be added using
+ * {@link MBeanServer#addNotificationListener(ObjectName, NotificationListener,
+ * NotificationFilter, Object) MBeanServer.addNotificationListener}. If
+ * {@code getDynamicMBeanFor} does not always return the same object for the
+ * same name, more work is needed to make notifications work, as described
+ * below.
+ *
+ * Namespaces containing Virtual MBeans
+ *
+ * Virtual MBeans are MBeans that do not exist as Java objects,
+ * except transiently while they are being accessed. This is useful when
+ * there might be very many of them, or when keeping track of their creation
+ * and deletion might be expensive or hard. For example, you might have one
+ * MBean per system process. With an ordinary {@code MBeanServer}, you would
+ * have to list the system processes in order to create an MBean object for
+ * each one, and you would have to track the arrival and departure of system
+ * processes in order to create or delete the corresponding MBeans. With
+ * Virtual MBeans, you only need the MBean for a given process at the exact
+ * point where it is referenced with a call such as
+ * {@link MBeanServer#getAttribute MBeanServer.getAttribute}.
+ *
+ * Here is an example of an {@code MBeanServer} implementation that has
+ * one MBean for every system property. The system property {@code "java.home"}
+ * is represented by the MBean called {@code
+ * com.example:type=Property,name="java.home"}, with an attribute called
+ * {@code Value} that is the value of the property.
+ *
+ *
+ * public interface PropertyMBean {
+ * public String getValue();
+ * }
+ *
+ * public class PropsMBS extends MBeanServerSupport {
+ * private static ObjectName newObjectName(String name) {
+ * try {
+ * return new ObjectName(name);
+ * } catch (MalformedObjectNameException e) {
+ * throw new AssertionError(e);
+ * }
+ * }
+ *
+ * public static class PropertyImpl implements PropertyMBean {
+ * private final String name;
+ *
+ * public PropertyImpl(String name) {
+ * this.name = name;
+ * }
+ *
+ * public String getValue() {
+ * return System.getProperty(name);
+ * }
+ * }
+ *
+ * @Override
+ * public DynamicMBean {@link #getDynamicMBeanFor
+ * getDynamicMBeanFor}(ObjectName name)
+ * throws InstanceNotFoundException {
+ *
+ * // Check that the name is a legal one for a Property MBean
+ * ObjectName namePattern = newObjectName(
+ * "com.example:type=Property,name=\"*\"");
+ * if (!namePattern.apply(name))
+ * throw new InstanceNotFoundException(name);
+ *
+ * // Extract the name of the property that the MBean corresponds to
+ * String propName = ObjectName.unquote(name.getKeyProperty("name"));
+ * if (System.getProperty(propName) == null)
+ * throw new InstanceNotFoundException(name);
+ *
+ * // Construct and return a transient MBean object
+ * PropertyMBean propMBean = new PropertyImpl(propName);
+ * return new StandardMBean(propMBean, PropertyMBean.class, false);
+ * }
+ *
+ * @Override
+ * protected {@code Set} {@link #getNames getNames}() {
+ * {@code Set names = new TreeSet();}
+ * Properties props = System.getProperties();
+ * for (String propName : props.stringPropertyNames()) {
+ * ObjectName objectName = newObjectName(
+ * "com.example:type=Property,name=" +
+ * ObjectName.quote(propName));
+ * names.add(objectName);
+ * }
+ * return names;
+ * }
+ * }
+ *
+ *
+ * Because the {@code getDynamicMBeanFor} method
+ * returns a different object every time it is called, the default handling
+ * of notifications will not work, as explained below.
+ * In this case it does not matter, because the object returned by {@code
+ * getDynamicMBeanFor} is not a {@code NotificationEmitter}, so {@link
+ * MBeanServer#addNotificationListener(ObjectName, NotificationListener,
+ * NotificationFilter, Object) MBeanServer.addNotificationListener} will
+ * always fail. But if we wanted to extend {@code PropsMBS} so that the MBean
+ * for property {@code "foo"} emitted a notification every time that property
+ * changed, we would need to do it as shown below. (Because there is no API to
+ * be informed when a property changes, this code assumes that some other code
+ * calls the {@code propertyChanged} method every time a property changes.)
+ *
+ *
+ * public class PropsMBS {
+ * ...as above...
+ *
+ * private final {@link VirtualEventManager} vem = new VirtualEventManager();
+ *
+ * @Override
+ * public NotificationEmitter {@link #getNotificationEmitterFor
+ * getNotificationEmitterFor}(
+ * ObjectName name) throws InstanceNotFoundException {
+ * getDynamicMBeanFor(name); // check that the name is valid
+ * return vem.{@link VirtualEventManager#getNotificationEmitterFor
+ * getNotificationEmitterFor}(name);
+ * }
+ *
+ * public void propertyChanged(String name, String newValue) {
+ * ObjectName objectName = newObjectName(
+ * "com.example:type=Property,name=" + ObjectName.quote(name));
+ * Notification n = new Notification(
+ * "com.example.property.changed", objectName, 0L,
+ * "Property " + name + " changed");
+ * n.setUserData(newValue);
+ * vem.{@link VirtualEventManager#publish publish}(objectName, n);
+ * }
+ * }
+ *
+ *
+ * MBean creation and deletion
+ *
+ * MBean creation through {@code MBeanServer.createMBean} is disabled
+ * by default. Subclasses which need to support MBean creation
+ * through {@code createMBean} need to implement a single method {@link
+ * #createMBean(String, ObjectName, ObjectName, Object[], String[],
+ * boolean)}.
+ *
+ * Similarly MBean registration and unregistration through {@code
+ * registerMBean} and {@code unregisterMBean} are disabled by default.
+ * Subclasses which need to support MBean registration and
+ * unregistration will need to implement {@link #registerMBean registerMBean}
+ * and {@link #unregisterMBean unregisterMBean}.
+ *
+ * Notifications
+ *
+ * By default {@link MBeanServer#addNotificationListener(ObjectName,
+ * NotificationListener, NotificationFilter, Object) addNotificationListener}
+ * is accepted for an MBean {@code name} if {@link #getDynamicMBeanFor
+ * getDynamicMBeanFor}(name)
returns an object that is a
+ * {@link NotificationEmitter}. That is appropriate if
+ * {@code getDynamicMBeanFor}(name)
always returns the
+ * same object for the same {@code name}. But with
+ * Virtual MBeans, every call to {@code getDynamicMBeanFor} returns a new object,
+ * which is discarded as soon as the MBean request has finished.
+ * So a listener added to that object would be immediately forgotten.
+ *
+ * The simplest way for a subclass that defines Virtual MBeans
+ * to support notifications is to create a private {@link VirtualEventManager}
+ * and override the method {@link
+ * #getNotificationEmitterFor getNotificationEmitterFor} as follows:
+ *
+ *
+ * private final VirtualEventManager vem = new VirtualEventManager();
+ *
+ * @Override
+ * public NotificationEmitter getNotificationEmitterFor(
+ * ObjectName name) throws InstanceNotFoundException {
+ * // Check that the name is a valid Virtual MBean.
+ * // This is the easiest way to do that, but not always the
+ * // most efficient:
+ * getDynamicMBeanFor(name);
+ *
+ * // Return an object that supports add/removeNotificationListener
+ * // through the VirtualEventManager.
+ * return vem.getNotificationEmitterFor(name);
+ * }
+ *
+ *
+ * A notification {@code n} can then be sent from the Virtual MBean
+ * called {@code name} by calling {@link VirtualEventManager#publish
+ * vem.publish}(name, n)
. See the example
+ * above.
+ *
+ * @since Java SE 7
+ */
+public abstract class MBeanServerSupport implements MBeanServer {
+
+ /**
+ * A logger for this class.
+ */
+ private static final Logger LOG =
+ Logger.getLogger(MBeanServerSupport.class.getName());
+
+ /**
+ * Make a new {@code MBeanServerSupport} instance.
+ */
+ protected MBeanServerSupport() {
+ }
+
+ /**
+ * Returns a dynamically created handle that makes it possible to
+ * access the named MBean for the duration of a method call.
+ *
+ * An easy way to create such a {@link DynamicMBean} handle is, for
+ * instance, to create a temporary MXBean instance and to wrap it in
+ * an instance of
+ * {@link javax.management.StandardMBean}.
+ * This handle should remain valid for the duration of the call
+ * but can then be discarded.
+ * @param name the name of the MBean for which a request was received.
+ * @return a {@link DynamicMBean} handle that can be used to invoke
+ * operations on the named MBean.
+ * @throws InstanceNotFoundException if no such MBean is supposed
+ * to exist.
+ */
+ public abstract DynamicMBean getDynamicMBeanFor(ObjectName name)
+ throws InstanceNotFoundException;
+
+ /**
+ * Subclasses should implement this method to return
+ * the names of all MBeans handled by this object instance.
+ *
+ * The object returned by getNames() should be safely {@linkplain
+ * Set#iterator iterable} even in the presence of other threads that may
+ * cause the set of names to change. Typically this means one of the
+ * following:
+ *
+ *
+ * - the returned set of names is always the same; or
+ *
- the returned set of names is an object such as a {@link
+ * java.util.concurrent.CopyOnWriteArraySet CopyOnWriteArraySet} that is
+ * safely iterable even if the set is changed by other threads; or
+ *
- a new Set is constructed every time this method is called.
+ *
+ *
+ * @return the names of all MBeans handled by this object.
+ */
+ protected abstract Set getNames();
+
+ /**
+ * List names matching the given pattern.
+ * The default implementation of this method calls {@link #getNames()}
+ * and returns the subset of those names matching {@code pattern}.
+ *
+ * @param pattern an ObjectName pattern
+ * @return the list of MBean names that match the given pattern.
+ */
+ protected Set getMatchingNames(ObjectName pattern) {
+ return Util.filterMatchingNames(pattern, getNames());
+ }
+
+ /**
+ * Returns a {@link NotificationEmitter} which can be used to
+ * subscribe or unsubscribe for notifications with the named
+ * mbean.
+ *
+ * The default implementation of this method calls {@link
+ * #getDynamicMBeanFor getDynamicMBeanFor(name)} and returns that object
+ * if it is a {@code NotificationEmitter}, otherwise null. See above for further discussion of notification
+ * handling.
+ *
+ * @param name The name of the MBean whose notifications are being
+ * subscribed, or unsuscribed.
+ *
+ * @return A {@link NotificationEmitter} that can be used to subscribe or
+ * unsubscribe for notifications emitted by the named MBean, or {@code
+ * null} if the MBean does not emit notifications and should not be
+ * considered as a {@code NotificationEmitter}.
+ *
+ * @throws InstanceNotFoundException if {@code name} is not the name of
+ * an MBean in this {@code MBeanServer}.
+ */
+ public NotificationEmitter getNotificationEmitterFor(ObjectName name)
+ throws InstanceNotFoundException {
+ DynamicMBean mbean = getDynamicMBeanFor(name);
+ if (mbean instanceof NotificationEmitter)
+ return (NotificationEmitter) mbean;
+ else
+ return null;
+ }
+
+ private NotificationEmitter getNonNullNotificationEmitterFor(
+ ObjectName name)
+ throws InstanceNotFoundException {
+ NotificationEmitter emitter = getNotificationEmitterFor(name);
+ if (emitter == null) {
+ IllegalArgumentException iae = new IllegalArgumentException(
+ "Not a NotificationEmitter: " + name);
+ throw new RuntimeOperationsException(iae);
+ }
+ return emitter;
+ }
+
+ /**
+ * Creates a new MBean in the MBean name space.
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws an {@link
+ * UnsupportedOperationException}
+ * wrapped in a {@link RuntimeOperationsException}.
+ *
+ * Subclasses may redefine this method to provide an implementation.
+ * All the various flavors of {@code MBeanServer.createMBean} methods
+ * will eventually call this method. A subclass that wishes to
+ * support MBean creation through {@code createMBean} thus only
+ * needs to provide an implementation for this one method.
+ *
+ * @param className The class name of the MBean to be instantiated.
+ * @param name The object name of the MBean. May be null.
+ * @param params An array containing the parameters of the
+ * constructor to be invoked.
+ * @param signature An array containing the signature of the
+ * constructor to be invoked.
+ * @param loaderName The object name of the class loader to be used.
+ * @param useCLR This parameter is {@code true} when this method
+ * is called from one of the {@code MBeanServer.createMBean} methods
+ * whose signature does not include the {@code ObjectName} of an
+ * MBean class loader to use for loading the MBean class.
+ *
+ * @return An ObjectInstance
, containing the
+ * ObjectName
and the Java class name of the newly
+ * instantiated MBean. If the contained ObjectName
+ * is n
, the contained Java class name is
+ * {@link javax.management.MBeanServer#getMBeanInfo
+ * getMBeanInfo(n)}.getClassName()
.
+ *
+ * @exception ReflectionException Wraps a
+ * java.lang.ClassNotFoundException
or a
+ * java.lang.Exception
that occurred when trying to
+ * invoke the MBean's constructor.
+ * @exception InstanceAlreadyExistsException The MBean is already
+ * under the control of the MBean server.
+ * @exception MBeanRegistrationException The
+ * preRegister
(MBeanRegistration
+ * interface) method of the MBean has thrown an exception. The
+ * MBean will not be registered.
+ * @exception MBeanException The constructor of the MBean has
+ * thrown an exception
+ * @exception NotCompliantMBeanException This class is not a JMX
+ * compliant MBean
+ * @exception InstanceNotFoundException The specified class loader
+ * is not registered in the MBean server.
+ * @exception RuntimeOperationsException Wraps either:
+ *
+ * - a
java.lang.IllegalArgumentException
: The className
+ * passed in parameter is null, the ObjectName
passed in
+ * parameter contains a pattern or no ObjectName
is specified
+ * for the MBean; or
+ * - an {@code UnsupportedOperationException} if creating MBeans is not
+ * supported by this {@code MBeanServer} implementation.
+ *
+ */
+ public ObjectInstance createMBean(String className,
+ ObjectName name, ObjectName loaderName, Object[] params,
+ String[] signature, boolean useCLR)
+ throws ReflectionException, InstanceAlreadyExistsException,
+ MBeanRegistrationException, MBeanException,
+ NotCompliantMBeanException, InstanceNotFoundException {
+ throw newUnsupportedException("createMBean");
+ }
+
+
+ /**
+ * Attempts to determine whether the named MBean should be
+ * considered as an instance of a given class. The default implementation
+ * of this method calls {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}
+ * to get an MBean object. Then its behaviour is the same as the standard
+ * {@link MBeanServer#isInstanceOf MBeanServer.isInstanceOf} method.
+ *
+ * {@inheritDoc}
+ */
+ public boolean isInstanceOf(ObjectName name, String className)
+ throws InstanceNotFoundException {
+
+ final DynamicMBean instance = nonNullMBeanFor(name);
+
+ try {
+ final String mbeanClassName = instance.getMBeanInfo().getClassName();
+
+ if (mbeanClassName.equals(className))
+ return true;
+
+ final Object resource;
+ final ClassLoader cl;
+ if (instance instanceof DynamicWrapperMBean) {
+ DynamicWrapperMBean d = (DynamicWrapperMBean) instance;
+ resource = d.getWrappedObject();
+ cl = d.getWrappedClassLoader();
+ } else {
+ resource = instance;
+ cl = instance.getClass().getClassLoader();
+ }
+
+ final Class> classNameClass = Class.forName(className, false, cl);
+
+ if (classNameClass.isInstance(resource))
+ return true;
+
+ if (classNameClass == NotificationBroadcaster.class ||
+ classNameClass == NotificationEmitter.class) {
+ try {
+ getNotificationEmitterFor(name);
+ return true;
+ } catch (Exception x) {
+ LOG.finest("MBean " + name +
+ " is not a notification emitter. Ignoring: "+x);
+ return false;
+ }
+ }
+
+ final Class> resourceClass = Class.forName(mbeanClassName, false, cl);
+ return classNameClass.isAssignableFrom(resourceClass);
+ } catch (Exception x) {
+ /* Could be SecurityException or ClassNotFoundException */
+ LOG.logp(Level.FINEST,
+ MBeanServerSupport.class.getName(),
+ "isInstanceOf", "Exception calling isInstanceOf", x);
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method returns the string
+ * "DefaultDomain".
+ */
+ public String getDefaultDomain() {
+ return "DefaultDomain";
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method returns
+ * {@link #getNames()}.size().
+ */
+ public Integer getMBeanCount() {
+ return getNames().size();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method first calls {@link #getNames
+ * getNames()} to get a list of all MBean names,
+ * and from this set of names, derives the set of domains which contain
+ * MBeans.
+ */
+ public String[] getDomains() {
+ final Set names = getNames();
+ final Set res = new TreeSet();
+ for (ObjectName n : names) {
+ if (n == null) continue; // not allowed but you never know.
+ res.add(n.getDomain());
+ }
+ return res.toArray(new String[res.size()]);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method will first
+ * call {@link
+ * #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a handle
+ * to the named MBean,
+ * and then call {@link DynamicMBean#getAttribute getAttribute}
+ * on that {@link DynamicMBean} handle.
+ *
+ * @throws RuntimeOperationsException {@inheritDoc}
+ */
+ public Object getAttribute(ObjectName name, String attribute)
+ throws MBeanException, AttributeNotFoundException,
+ InstanceNotFoundException, ReflectionException {
+ final DynamicMBean mbean = nonNullMBeanFor(name);
+ return mbean.getAttribute(attribute);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method will first
+ * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}
+ * to obtain a handle to the named MBean,
+ * and then call {@link DynamicMBean#setAttribute setAttribute}
+ * on that {@link DynamicMBean} handle.
+ *
+ * @throws RuntimeOperationsException {@inheritDoc}
+ */
+ public void setAttribute(ObjectName name, Attribute attribute)
+ throws InstanceNotFoundException, AttributeNotFoundException,
+ InvalidAttributeValueException, MBeanException,
+ ReflectionException {
+ final DynamicMBean mbean = nonNullMBeanFor(name);
+ mbean.setAttribute(attribute);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method will first
+ * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
+ * handle to the named MBean,
+ * and then call {@link DynamicMBean#getAttributes getAttributes}
+ * on that {@link DynamicMBean} handle.
+ *
+ * @throws RuntimeOperationsException {@inheritDoc}
+ */
+ public AttributeList getAttributes(ObjectName name,
+ String[] attributes) throws InstanceNotFoundException,
+ ReflectionException {
+ final DynamicMBean mbean = nonNullMBeanFor(name);
+ return mbean.getAttributes(attributes);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method will first
+ * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
+ * handle to the named MBean,
+ * and then call {@link DynamicMBean#setAttributes setAttributes}
+ * on that {@link DynamicMBean} handle.
+ *
+ * @throws RuntimeOperationsException {@inheritDoc}
+ */
+ public AttributeList setAttributes(ObjectName name, AttributeList attributes)
+ throws InstanceNotFoundException, ReflectionException {
+ final DynamicMBean mbean = nonNullMBeanFor(name);
+ return mbean.setAttributes(attributes);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method will first
+ * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
+ * handle to the named MBean,
+ * and then call {@link DynamicMBean#invoke invoke}
+ * on that {@link DynamicMBean} handle.
+ */
+ public Object invoke(ObjectName name, String operationName,
+ Object[] params, String[] signature)
+ throws InstanceNotFoundException, MBeanException,
+ ReflectionException {
+ final DynamicMBean mbean = nonNullMBeanFor(name);
+ return mbean.invoke(operationName, params, signature);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method will first
+ * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
+ * handle to the named MBean,
+ * and then call {@link DynamicMBean#getMBeanInfo getMBeanInfo}
+ * on that {@link DynamicMBean} handle.
+ */
+ public MBeanInfo getMBeanInfo(ObjectName name)
+ throws InstanceNotFoundException, IntrospectionException,
+ ReflectionException {
+ final DynamicMBean mbean = nonNullMBeanFor(name);
+ return mbean.getMBeanInfo();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method will call
+ * {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}.{@link DynamicMBean#getMBeanInfo getMBeanInfo()}.{@link MBeanInfo#getClassName getClassName()} to get the
+ * class name to combine with {@code name} to produce a new
+ * {@code ObjectInstance}.
+ */
+ public ObjectInstance getObjectInstance(ObjectName name)
+ throws InstanceNotFoundException {
+ final DynamicMBean mbean = nonNullMBeanFor(name);
+ final String className = mbean.getMBeanInfo().getClassName();
+ return new ObjectInstance(name, className);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method will first call {@link
+ * #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a handle to the
+ * named MBean. If {@code getDynamicMBeanFor} returns an object, {@code
+ * isRegistered} will return true. If {@code getDynamicMBeanFor} returns
+ * null or throws {@link InstanceNotFoundException}, {@code isRegistered}
+ * will return false.
+ *
+ * @throws RuntimeOperationsException {@inheritDoc}
+ */
+ public boolean isRegistered(ObjectName name) {
+ try {
+ final DynamicMBean mbean = getDynamicMBeanFor(name);
+ return mbean!=null;
+ } catch (InstanceNotFoundException x) {
+ if (LOG.isLoggable(Level.FINEST))
+ LOG.finest("MBean "+name+" is not registered: "+x);
+ return false;
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method will first
+ * call {@link #queryNames queryNames}
+ * to get a list of all matching MBeans, and then, for each returned name,
+ * call {@link #getObjectInstance getObjectInstance(name)}.
+ */
+ public Set queryMBeans(ObjectName pattern, QueryExp query) {
+ final Set names = queryNames(pattern, query);
+ if (names.isEmpty()) return Collections.emptySet();
+ final Set mbeans = new HashSet();
+ for (ObjectName name : names) {
+ try {
+ mbeans.add(getObjectInstance(name));
+ } catch (SecurityException x) { // DLS: OK
+ continue;
+ } catch (InstanceNotFoundException x) { // DLS: OK
+ continue;
+ }
+ }
+ return mbeans;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method calls {@link #getMatchingNames
+ * getMatchingNames(pattern)} to obtain a list of MBeans matching
+ * the given name pattern. If the {@code query} parameter is null,
+ * this will be the result. Otherwise, it will evaluate the
+ * {@code query} parameter for each of the returned names, exactly
+ * as an {@code MBeanServer} would. This might result in
+ * {@link #getDynamicMBeanFor getDynamicMBeanFor} being called
+ * several times for each returned name.
+ */
+ public Set queryNames(ObjectName pattern, QueryExp query) {
+ try {
+ final Set res = getMatchingNames(pattern);
+ return filterListOfObjectNames(res, query);
+ } catch (Exception x) {
+ LOG.fine("Unexpected exception raised in queryNames: "+x);
+ LOG.log(Level.FINEST, "Unexpected exception raised in queryNames", x);
+ }
+ // We reach here only when an exception was raised.
+ //
+ return Collections.emptySet();
+ }
+
+ private final static boolean apply(final QueryExp query,
+ final ObjectName on,
+ final MBeanServer srv) {
+ boolean res = false;
+ MBeanServer oldServer = QueryEval.getMBeanServer();
+ query.setMBeanServer(srv);
+ try {
+ res = query.apply(on);
+ } catch (Exception e) {
+ LOG.finest("QueryExp.apply threw exception, returning false." +
+ " Cause: "+e);
+ res = false;
+ } finally {
+ /*
+ * query.setMBeanServer is probably
+ * QueryEval.setMBeanServer so put back the old
+ * value. Since that method uses a ThreadLocal
+ * variable, this code is only needed for the
+ * unusual case where the user creates a custom
+ * QueryExp that calls a nested query on another
+ * MBeanServer.
+ */
+ query.setMBeanServer(oldServer);
+ }
+ return res;
+ }
+
+ /**
+ * Filters a {@code Set} according to a pattern and a query.
+ * This might be quite inefficient for virtual name spaces.
+ */
+ Set
+ filterListOfObjectNames(Set list,
+ QueryExp query) {
+ if (list.isEmpty() || query == null)
+ return list;
+
+ // create a new result set
+ final Set result = new HashSet();
+
+ for (ObjectName on : list) {
+ // if on doesn't match query exclude it.
+ if (apply(query, on, this))
+ result.add(on);
+ }
+ return result;
+ }
+
+
+ // Don't use {@inheritDoc}, because we don't want to say that the
+ // MBeanServer replaces a reference to the MBean by its ObjectName.
+ /**
+ * Adds a listener to a registered MBean. A notification emitted by
+ * the MBean will be forwarded to the listener.
+ *
+ * This implementation calls
+ * {@link #getNotificationEmitterFor getNotificationEmitterFor}
+ * and invokes {@code addNotificationListener} on the
+ * {@link NotificationEmitter} it returns.
+ *
+ * @see #getDynamicMBeanFor getDynamicMBeanFor
+ * @see #getNotificationEmitterFor getNotificationEmitterFor
+ */
+ public void addNotificationListener(ObjectName name,
+ NotificationListener listener, NotificationFilter filter,
+ Object handback) throws InstanceNotFoundException {
+ final NotificationEmitter emitter =
+ getNonNullNotificationEmitterFor(name);
+ emitter.addNotificationListener(listener, filter, handback);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ *
This implementation calls
+ * {@link #getNotificationEmitterFor getNotificationEmitterFor}
+ * and invokes {@code removeNotificationListener} on the
+ * {@link NotificationEmitter} it returns.
+ * @see #getDynamicMBeanFor getDynamicMBeanFor
+ * @see #getNotificationEmitterFor getNotificationEmitterFor
+ */
+ public void removeNotificationListener(ObjectName name,
+ NotificationListener listener)
+ throws InstanceNotFoundException, ListenerNotFoundException {
+ final NotificationEmitter emitter =
+ getNonNullNotificationEmitterFor(name);
+ emitter.removeNotificationListener(listener);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ *
This implementation calls
+ * {@link #getNotificationEmitterFor getNotificationEmitterFor}
+ * and invokes {@code removeNotificationListener} on the
+ * {@link NotificationEmitter} it returns.
+ * @see #getDynamicMBeanFor getDynamicMBeanFor
+ * @see #getNotificationEmitterFor getNotificationEmitterFor
+ */
+ public void removeNotificationListener(ObjectName name,
+ NotificationListener listener, NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException, ListenerNotFoundException {
+ NotificationEmitter emitter =
+ getNonNullNotificationEmitterFor(name);
+ emitter.removeNotificationListener(listener);
+ }
+
+
+ /**
+ *
Adds a listener to a registered MBean.
+ *
+ * The default implementation of this method first calls
+ * {@link #getDynamicMBeanFor getDynamicMBeanFor(listenerName)}.
+ * If that successfully returns an object, call it {@code
+ * mbean}, then (a) if {@code mbean} is an instance of {@link
+ * NotificationListener} then this method calls {@link
+ * #addNotificationListener(ObjectName, NotificationListener,
+ * NotificationFilter, Object) addNotificationListener(name, mbean, filter,
+ * handback)}, otherwise (b) this method throws an exception as specified
+ * for this case.
+ *
+ * This default implementation is not appropriate for Virtual MBeans,
+ * although that only matters if the object returned by {@code
+ * getDynamicMBeanFor} can be an instance of
+ * {@code NotificationListener}.
+ *
+ * @throws RuntimeOperationsException {@inheritDoc}
+ */
+ public void addNotificationListener(ObjectName name, ObjectName listenerName,
+ NotificationFilter filter, Object handback)
+ throws InstanceNotFoundException {
+ NotificationListener listener = getListenerMBean(listenerName);
+ addNotificationListener(name, listener, filter, handback);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link RuntimeOperationsException} wrapping
+ * {@link UnsupportedOperationException}.
+ *
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ public void removeNotificationListener(ObjectName name,
+ ObjectName listenerName)
+ throws InstanceNotFoundException, ListenerNotFoundException {
+ NotificationListener listener = getListenerMBean(listenerName);
+ removeNotificationListener(name, listener);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link RuntimeOperationsException} wrapping
+ * {@link UnsupportedOperationException}.
+ *
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ public void removeNotificationListener(ObjectName name,
+ ObjectName listenerName, NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException, ListenerNotFoundException {
+ NotificationListener listener = getListenerMBean(listenerName);
+ removeNotificationListener(name, listener, filter, handback);
+ }
+
+ private NotificationListener getListenerMBean(ObjectName listenerName)
+ throws InstanceNotFoundException {
+ Object mbean = getDynamicMBeanFor(listenerName);
+ if (mbean instanceof NotificationListener)
+ return (NotificationListener) mbean;
+ else {
+ throw newIllegalArgumentException(
+ "MBean is not a NotificationListener: " + listenerName);
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link InstanceNotFoundException} wrapping
+ * {@link UnsupportedOperationException}.
+ *
+ * @return the default implementation of this method never returns.
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ public ClassLoader getClassLoader(ObjectName loaderName)
+ throws InstanceNotFoundException {
+ final UnsupportedOperationException failed =
+ new UnsupportedOperationException("getClassLoader");
+ final InstanceNotFoundException x =
+ new InstanceNotFoundException(String.valueOf(loaderName));
+ x.initCause(failed);
+ throw x;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method calls
+ * {@link #getDynamicMBeanFor getDynamicMBeanFor(mbeanName)} and applies
+ * the logic just described to the result.
+ */
+ public ClassLoader getClassLoaderFor(ObjectName mbeanName)
+ throws InstanceNotFoundException {
+ final DynamicMBean mbean = nonNullMBeanFor(mbeanName);
+ if (mbean instanceof DynamicWrapperMBean)
+ return ((DynamicWrapperMBean) mbean).getWrappedClassLoader();
+ else
+ return mbean.getClass().getClassLoader();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation of this method returns a
+ * {@link ClassLoaderRepository} containing exactly one loader,
+ * the {@linkplain Thread#getContextClassLoader() context class loader}
+ * for the current thread.
+ * Subclasses can override this method to return a different
+ * {@code ClassLoaderRepository}.
+ */
+ public ClassLoaderRepository getClassLoaderRepository() {
+ // We return a new ClassLoaderRepository each time this
+ // method is called. This is by design, because the
+ // SingletonClassLoaderRepository is a very small object and
+ // getClassLoaderRepository() will not be called very often
+ // (the connector server calls it once) - in the context of
+ // MBeanServerSupport there's a very good chance that this method will
+ // *never* be called.
+ ClassLoader ccl = Thread.currentThread().getContextClassLoader();
+ return Util.getSingleClassLoaderRepository(ccl);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link RuntimeOperationsException} wrapping
+ * {@link UnsupportedOperationException}.
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ public ObjectInstance registerMBean(Object object, ObjectName name)
+ throws InstanceAlreadyExistsException, MBeanRegistrationException,
+ NotCompliantMBeanException {
+ throw newUnsupportedException("registerMBean");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link RuntimeOperationsException} wrapping
+ * {@link UnsupportedOperationException}.
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ public void unregisterMBean(ObjectName name)
+ throws InstanceNotFoundException, MBeanRegistrationException {
+ throw newUnsupportedException("unregisterMBean");
+ }
+
+ /**
+ * Calls {@link #createMBean(String, ObjectName,
+ * ObjectName, Object[], String[], boolean)
+ * createMBean(className, name, null, params, signature, true)};
+ */
+ public final ObjectInstance createMBean(String className, ObjectName name,
+ Object[] params, String[] signature)
+ throws ReflectionException, InstanceAlreadyExistsException,
+ MBeanRegistrationException, MBeanException,
+ NotCompliantMBeanException {
+ try {
+ return safeCreateMBean(className, name, null, params, signature, true);
+ } catch (InstanceNotFoundException ex) {
+ // should not happen!
+ throw new MBeanException(ex, "Unexpected exception: " + ex);
+ }
+ }
+
+ /**
+ * Calls {@link #createMBean(String, ObjectName,
+ * ObjectName, Object[], String[], boolean)
+ * createMBean(className,name, loaderName, params, signature, false)};
+ */
+ public final ObjectInstance createMBean(String className, ObjectName name,
+ ObjectName loaderName, Object[] params, String[] signature)
+ throws ReflectionException, InstanceAlreadyExistsException,
+ MBeanRegistrationException, MBeanException,
+ NotCompliantMBeanException, InstanceNotFoundException {
+ return safeCreateMBean(className, name, loaderName, params, signature, false);
+ }
+
+ /**
+ * Calls {@link #createMBean(String, ObjectName,
+ * ObjectName, Object[], String[], boolean)
+ * createMBean(className, name, null, null, null, true)};
+ */
+ public final ObjectInstance createMBean(String className, ObjectName name)
+ throws ReflectionException, InstanceAlreadyExistsException,
+ MBeanRegistrationException, MBeanException,
+ NotCompliantMBeanException {
+ try {
+ return safeCreateMBean(className, name, null, null, null, true);
+ } catch (InstanceNotFoundException ex) {
+ // should not happen!
+ throw new MBeanException(ex, "Unexpected exception: " + ex);
+ }
+ }
+
+ /**
+ * Calls {@link #createMBean(String, ObjectName,
+ * ObjectName, Object[], String[], boolean)
+ * createMBean(className, name, loaderName, null, null, false)};
+ */
+ public final ObjectInstance createMBean(String className, ObjectName name,
+ ObjectName loaderName)
+ throws ReflectionException, InstanceAlreadyExistsException,
+ MBeanRegistrationException, MBeanException,
+ NotCompliantMBeanException, InstanceNotFoundException {
+ return safeCreateMBean(className, name, loaderName, null, null, false);
+ }
+
+ // make sure all exceptions are correctly wrapped in a JMXException
+ private ObjectInstance safeCreateMBean(String className,
+ ObjectName name, ObjectName loaderName, Object[] params,
+ String[] signature, boolean useRepository)
+ throws ReflectionException, InstanceAlreadyExistsException,
+ MBeanRegistrationException, MBeanException,
+ NotCompliantMBeanException, InstanceNotFoundException {
+ try {
+ return createMBean(className, name, loaderName, params,
+ signature, useRepository);
+ } catch (ReflectionException x) { throw x;
+ } catch (InstanceAlreadyExistsException x) { throw x;
+ } catch (MBeanRegistrationException x) { throw x;
+ } catch (MBeanException x) { throw x;
+ } catch (NotCompliantMBeanException x) { throw x;
+ } catch (InstanceNotFoundException x) { throw x;
+ } catch (SecurityException x) { throw x;
+ } catch (JMRuntimeException x) { throw x;
+ } catch (RuntimeException x) {
+ throw new RuntimeOperationsException(x, x.toString());
+ } catch (Exception x) {
+ throw new MBeanException(x, x.toString());
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ *
This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link RuntimeOperationsException} wrapping
+ * {@link UnsupportedOperationException}.
+ *
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ public Object instantiate(String className)
+ throws ReflectionException, MBeanException {
+ throw new UnsupportedOperationException("Not applicable.");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link RuntimeOperationsException} wrapping
+ * {@link UnsupportedOperationException}.
+ *
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ public Object instantiate(String className, ObjectName loaderName)
+ throws ReflectionException, MBeanException,
+ InstanceNotFoundException {
+ throw new UnsupportedOperationException("Not applicable.");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link RuntimeOperationsException} wrapping
+ * {@link UnsupportedOperationException}.
+ *
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ public Object instantiate(String className, Object[] params,
+ String[] signature) throws ReflectionException, MBeanException {
+ throw new UnsupportedOperationException("Not applicable.");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link RuntimeOperationsException} wrapping
+ * {@link UnsupportedOperationException}.
+ *
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ public Object instantiate(String className, ObjectName loaderName,
+ Object[] params, String[] signature)
+ throws ReflectionException, MBeanException,
+ InstanceNotFoundException {
+ throw new UnsupportedOperationException("Not applicable.");
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link RuntimeOperationsException} wrapping
+ * {@link UnsupportedOperationException}.
+ *
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ @Deprecated
+ public ObjectInputStream deserialize(ObjectName name, byte[] data)
+ throws InstanceNotFoundException, OperationsException {
+ throw new UnsupportedOperationException("Not applicable.");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link RuntimeOperationsException} wrapping
+ * {@link UnsupportedOperationException}.
+ *
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ @Deprecated
+ public ObjectInputStream deserialize(String className, byte[] data)
+ throws OperationsException, ReflectionException {
+ throw new UnsupportedOperationException("Not applicable.");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This operation is not supported in this base class implementation.
+ * The default implementation of this method always throws
+ * {@link RuntimeOperationsException} wrapping
+ * {@link UnsupportedOperationException}.
+ *
+ * @throws javax.management.RuntimeOperationsException wrapping
+ * {@link UnsupportedOperationException}
+ */
+ @Deprecated
+ public ObjectInputStream deserialize(String className,
+ ObjectName loaderName, byte[] data)
+ throws InstanceNotFoundException, OperationsException,
+ ReflectionException {
+ throw new UnsupportedOperationException("Not applicable.");
+ }
+
+
+ // Calls getDynamicMBeanFor, and throws an InstanceNotFoundException
+ // if the returned mbean is null.
+ // The DynamicMBean returned by this method is thus guaranteed to be
+ // non null.
+ //
+ private DynamicMBean nonNullMBeanFor(ObjectName name)
+ throws InstanceNotFoundException {
+ if (name == null)
+ throw newIllegalArgumentException("Null ObjectName");
+ if (name.getDomain().equals("")) {
+ String defaultDomain = getDefaultDomain();
+ try {
+ // XXX change to ObjectName.switchDomain
+ // current code DOES NOT PRESERVE the order of keys
+ name = new ObjectName(defaultDomain, name.getKeyPropertyList());
+ } catch (Exception e) {
+ throw newIllegalArgumentException(
+ "Illegal default domain: " + defaultDomain);
+ }
+ }
+ final DynamicMBean mbean = getDynamicMBeanFor(name);
+ if (mbean!=null) return mbean;
+ throw new InstanceNotFoundException(String.valueOf(name));
+ }
+
+ static RuntimeException newUnsupportedException(String operation) {
+ return new RuntimeOperationsException(
+ new UnsupportedOperationException(
+ operation+": Not supported in this namespace"));
+ }
+
+ static RuntimeException newIllegalArgumentException(String msg) {
+ return new RuntimeOperationsException(
+ new IllegalArgumentException(msg));
+ }
+
+}
diff --git a/jdk/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java b/jdk/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java
new file mode 100644
index 00000000000..ec53998f51a
--- /dev/null
+++ b/jdk/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
+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.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.remote.IdentityMBeanServerForwarder;
+
+public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
+
+ private final ObjectName mbeanName;
+ private DynamicMBean mbean;
+
+ private MBeanServer mbeanMBS = new MBeanServerSupport() {
+
+ @Override
+ public DynamicMBean getDynamicMBeanFor(ObjectName name)
+ throws InstanceNotFoundException {
+ if (mbeanName.equals(name)) {
+ return mbean;
+ } else {
+ throw new InstanceNotFoundException(name.toString());
+ }
+ }
+
+ @Override
+ protected Set getNames() {
+ return Collections.singleton(mbeanName);
+ }
+
+ @Override
+ public NotificationEmitter getNotificationEmitterFor(
+ ObjectName name) {
+ if (mbean instanceof NotificationEmitter)
+ return (NotificationEmitter) mbean;
+ return null;
+ }
+
+ };
+
+ public SingleMBeanForwarder(ObjectName mbeanName, DynamicMBean mbean) {
+ this.mbeanName = mbeanName;
+ setSingleMBean(mbean);
+ }
+
+ protected void setSingleMBean(DynamicMBean mbean) {
+ this.mbean = mbean;
+ }
+
+ @Override
+ public void addNotificationListener(ObjectName name, ObjectName listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException {
+ if (mbeanName.equals(name))
+ mbeanMBS.addNotificationListener(name, listener, filter, handback);
+ else
+ super.addNotificationListener(name, listener, filter, handback);
+ }
+
+ @Override
+ public void addNotificationListener(ObjectName name,
+ NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException {
+ if (mbeanName.equals(name))
+ mbeanMBS.addNotificationListener(name, listener, filter, handback);
+ else
+ super.addNotificationListener(name, listener, filter, handback);
+ }
+
+ @Override
+ public ObjectInstance createMBean(String className, ObjectName name,
+ ObjectName loaderName, Object[] params,
+ String[] signature)
+ throws ReflectionException,
+ InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ MBeanException,
+ NotCompliantMBeanException,
+ InstanceNotFoundException {
+ if (mbeanName.equals(name))
+ throw new InstanceAlreadyExistsException(mbeanName.toString());
+ else
+ return super.createMBean(className, name, loaderName, params, signature);
+ }
+
+ @Override
+ public ObjectInstance createMBean(String className, ObjectName name,
+ Object[] params, String[] signature)
+ throws ReflectionException, InstanceAlreadyExistsException,
+ MBeanRegistrationException, MBeanException,
+ NotCompliantMBeanException {
+ if (mbeanName.equals(name))
+ throw new InstanceAlreadyExistsException(mbeanName.toString());
+ return super.createMBean(className, name, params, signature);
+ }
+
+ @Override
+ public ObjectInstance createMBean(String className, ObjectName name,
+ ObjectName loaderName)
+ throws ReflectionException,
+ InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ MBeanException,
+ NotCompliantMBeanException,
+ InstanceNotFoundException {
+ if (mbeanName.equals(name))
+ throw new InstanceAlreadyExistsException(mbeanName.toString());
+ return super.createMBean(className, name, loaderName);
+ }
+
+ @Override
+ public ObjectInstance createMBean(String className, ObjectName name)
+ throws ReflectionException,
+ InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ MBeanException,
+ NotCompliantMBeanException {
+ if (mbeanName.equals(name))
+ throw new InstanceAlreadyExistsException(mbeanName.toString());
+ return super.createMBean(className, name);
+ }
+
+ @Override
+ public Object getAttribute(ObjectName name, String attribute)
+ throws MBeanException,
+ AttributeNotFoundException,
+ InstanceNotFoundException,
+ ReflectionException {
+ if (mbeanName.equals(name))
+ return mbeanMBS.getAttribute(name, attribute);
+ else
+ return super.getAttribute(name, attribute);
+ }
+
+ @Override
+ public AttributeList getAttributes(ObjectName name, String[] attributes)
+ throws InstanceNotFoundException, ReflectionException {
+ if (mbeanName.equals(name))
+ return mbeanMBS.getAttributes(name, attributes);
+ else
+ return super.getAttributes(name, attributes);
+ }
+
+ @Override
+ public ClassLoader getClassLoader(ObjectName loaderName)
+ throws InstanceNotFoundException {
+ if (mbeanName.equals(loaderName))
+ return mbeanMBS.getClassLoader(loaderName);
+ else
+ return super.getClassLoader(loaderName);
+ }
+
+ @Override
+ public ClassLoader getClassLoaderFor(ObjectName name)
+ throws InstanceNotFoundException {
+ if (mbeanName.equals(name))
+ return mbeanMBS.getClassLoaderFor(name);
+ else
+ return super.getClassLoaderFor(name);
+ }
+
+ @Override
+ public String[] getDomains() {
+ TreeSet domainSet =
+ new TreeSet(Arrays.asList(super.getDomains()));
+ domainSet.add(mbeanName.getDomain());
+ return domainSet.toArray(new String[domainSet.size()]);
+ }
+
+ @Override
+ public Integer getMBeanCount() {
+ Integer count = super.getMBeanCount();
+ if (!super.isRegistered(mbeanName))
+ count++;
+ return count;
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo(ObjectName name)
+ throws InstanceNotFoundException,
+ IntrospectionException,
+ ReflectionException {
+ if (mbeanName.equals(name))
+ return mbeanMBS.getMBeanInfo(name);
+ else
+ return super.getMBeanInfo(name);
+ }
+
+ @Override
+ public ObjectInstance getObjectInstance(ObjectName name)
+ throws InstanceNotFoundException {
+ if (mbeanName.equals(name))
+ return mbeanMBS.getObjectInstance(name);
+ else
+ return super.getObjectInstance(name);
+ }
+
+ @Override
+ public Object invoke(ObjectName name, String operationName, Object[] params,
+ String[] signature)
+ throws InstanceNotFoundException,
+ MBeanException,
+ ReflectionException {
+ if (mbeanName.equals(name))
+ return mbeanMBS.invoke(name, operationName, params, signature);
+ else
+ return super.invoke(name, operationName, params, signature);
+ }
+
+ @Override
+ public boolean isInstanceOf(ObjectName name, String className)
+ throws InstanceNotFoundException {
+ if (mbeanName.equals(name))
+ return mbeanMBS.isInstanceOf(name, className);
+ else
+ return super.isInstanceOf(name, className);
+ }
+
+ @Override
+ public boolean isRegistered(ObjectName name) {
+ if (mbeanName.equals(name))
+ return true;
+ else
+ return super.isRegistered(name);
+ }
+
+ /**
+ * This is a ugly hack. Although jmx.context//*:* matches jmx.context//:*
+ * queryNames(jmx.context//*:*,null) must not return jmx.context//:*
+ * @param pattern the pattern to match against. must not be null.
+ * @return true if mbeanName can be included, false if it must not.
+ */
+ private boolean applies(ObjectName pattern) {
+ // we know pattern is not null.
+ if (!pattern.apply(mbeanName))
+ return false;
+
+// final String dompat = pattern.getDomain();
+// if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
+// return true; // We already checked that patterns apply.
+//
+// if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
+// // only matches if pattern ends with //
+// return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR);
+// }
+
+ // should not come here, unless mbeanName contains a // in the
+ // middle of its domain, which would be weird.
+ // let query on mbeanMBS proceed and take care of that.
+ //
+ return true;
+ }
+
+ @Override
+ public Set queryMBeans(ObjectName name, QueryExp query) {
+ Set names = super.queryMBeans(name, query);
+ if (name == null || applies(name) ) {
+ // Don't assume mbs.queryNames returns a writable set.
+ names = Util.cloneSet(names);
+ names.addAll(mbeanMBS.queryMBeans(name, query));
+ }
+ return names;
+ }
+
+ @Override
+ public Set queryNames(ObjectName name, QueryExp query) {
+ Set names = super.queryNames(name, query);
+ if (name == null || applies(name)) {
+ // Don't assume mbs.queryNames returns a writable set.
+ names = Util.cloneSet(names);
+ names.addAll(mbeanMBS.queryNames(name, query));
+ }
+ return names;
+ }
+
+
+ @Override
+ public ObjectInstance registerMBean(Object object, ObjectName name)
+ throws InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ NotCompliantMBeanException {
+ if (mbeanName.equals(name))
+ throw new InstanceAlreadyExistsException(mbeanName.toString());
+ else
+ return super.registerMBean(object, name);
+ }
+
+ @Override
+ public void removeNotificationListener(ObjectName name,
+ NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException,
+ ListenerNotFoundException {
+ if (mbeanName.equals(name))
+ mbeanMBS.removeNotificationListener(name, listener, filter, handback);
+ else
+ super.removeNotificationListener(name, listener, filter, handback);
+ }
+
+ @Override
+ public void removeNotificationListener(ObjectName name,
+ NotificationListener listener)
+ throws InstanceNotFoundException, ListenerNotFoundException {
+ if (mbeanName.equals(name))
+ mbeanMBS.removeNotificationListener(name, listener);
+ else
+ super.removeNotificationListener(name, listener);
+ }
+
+ @Override
+ public void removeNotificationListener(ObjectName name, ObjectName listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException,
+ ListenerNotFoundException {
+ if (mbeanName.equals(name))
+ mbeanMBS.removeNotificationListener(name, listener, filter, handback);
+ else
+ super.removeNotificationListener(name, listener, filter, handback);
+ }
+
+ @Override
+ public void removeNotificationListener(ObjectName name, ObjectName listener)
+ throws InstanceNotFoundException, ListenerNotFoundException {
+ if (mbeanName.equals(name))
+ mbeanMBS.removeNotificationListener(name, listener);
+ else
+ super.removeNotificationListener(name, listener);
+ }
+
+ @Override
+ public void setAttribute(ObjectName name, Attribute attribute)
+ throws InstanceNotFoundException,
+ AttributeNotFoundException,
+ InvalidAttributeValueException,
+ MBeanException,
+ ReflectionException {
+ if (mbeanName.equals(name))
+ mbeanMBS.setAttribute(name, attribute);
+ else
+ super.setAttribute(name, attribute);
+ }
+
+ @Override
+ public AttributeList setAttributes(ObjectName name,
+ AttributeList attributes)
+ throws InstanceNotFoundException, ReflectionException {
+ if (mbeanName.equals(name))
+ return mbeanMBS.setAttributes(name, attributes);
+ else
+ return super.setAttributes(name, attributes);
+ }
+
+ @Override
+ public void unregisterMBean(ObjectName name)
+ throws InstanceNotFoundException,
+ MBeanRegistrationException {
+ if (mbeanName.equals(name))
+ mbeanMBS.unregisterMBean(name);
+ else
+ super.unregisterMBean(name);
+ }
+}
diff --git a/jdk/src/share/classes/com/sun/jmx/interceptor/package.html b/jdk/src/share/classes/com/sun/jmx/interceptor/package.html
index 854436a9404..ad2163aa026 100644
--- a/jdk/src/share/classes/com/sun/jmx/interceptor/package.html
+++ b/jdk/src/share/classes/com/sun/jmx/interceptor/package.html
@@ -29,5 +29,8 @@ have any questions.
Provides specific classes to Sun JMX Reference Implementation.
+
+ This API is a Sun internal API and is subject to changes without notice.
+