6730926: Document that create/registerMBean can throw RuntimeMBeanException from postRegister
Reviewed-by: emcmanus
--- a/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java Tue Jul 29 16:57:09 2008 -0700
+++ b/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java Thu Jul 31 12:41:35 2008 +0200
@@ -453,11 +453,12 @@
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 @@
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 @@
}
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 @@
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 @@
}
}
- 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");
}
}
--- a/jdk/src/share/classes/javax/management/MBeanRegistration.java Tue Jul 29 16:57:09 2008 -0700
+++ b/jdk/src/share/classes/javax/management/MBeanRegistration.java Thu Jul 31 12:41:35 2008 +0200
@@ -158,7 +158,19 @@
/**
* Allows the MBean to perform any operations needed after having been
* registered in the MBean server or after the registration has failed.
- *
+ * <p>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. </p>
+ * <p>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.</p>
* @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 @@
/**
* Allows the MBean to perform any operations needed after having been
* unregistered in the MBean server.
+ * <p>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. </p>
+ * <p>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.</p>
*/
public void postDeregister();
--- a/jdk/src/share/classes/javax/management/MBeanServer.java Tue Jul 29 16:57:09 2008 -0700
+++ b/jdk/src/share/classes/javax/management/MBeanServer.java Thu Jul 31 12:41:35 2008 +0200
@@ -328,11 +328,30 @@
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
+ * @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
+ * <CODE>RuntimeException</CODE>, the <CODE>registerMBean<CODE> method will
+ * throw a <CODE>RuntimeMBeanException</CODE>, although the MBean
+ * registration succeeded. In such a case, the MBean will be actually
+ * registered even though the <CODE>registerMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
+ * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
+ * will not be registered.
+ * @exception RuntimeErrorException If the <CODE>postRegister</CODE>
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
+ * <CODE>Error</CODE>, the <CODE>registerMBean<CODE> method will
+ * throw a <CODE>RuntimeErrorException</CODE>, although the MBean
+ * registration succeeded. In such a case, the MBean will be actually
+ * registered even though the <CODE>registerMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
+ * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
+ * will not be registered.
* @exception NotCompliantMBeanException This object is not a JMX
* compliant MBean
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: 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,
--- a/jdk/src/share/classes/javax/management/MBeanServerConnection.java Tue Jul 29 16:57:09 2008 -0700
+++ b/jdk/src/share/classes/javax/management/MBeanServerConnection.java Thu Jul 31 12:41:35 2008 +0200
@@ -75,6 +75,24 @@
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
+ * @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
+ * <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
+ * throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the <CODE>createMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
+ * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
+ * will not be registered.
+ * @exception RuntimeErrorException If the <CODE>postRegister</CODE>
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
+ * <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
+ * throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the <CODE>createMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
+ * also be thrown by <CODE>preRegister</CODE>, 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 @@
* 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 @@
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
+ * @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
+ * <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
+ * throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the <CODE>createMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
+ * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
+ * will not be registered.
+ * @exception RuntimeErrorException If the <CODE>postRegister</CODE>
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
+ * <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
+ * throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the <CODE>createMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
+ * also be thrown by <CODE>preRegister</CODE>, 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 @@
* 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 @@
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
+ * @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
+ * <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
+ * throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the <CODE>createMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
+ * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
+ * will not be registered.
+ * @exception RuntimeErrorException If the <CODE>postRegister</CODE>
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
+ * <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
+ * throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the <CODE>createMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
+ * also be thrown by <CODE>preRegister</CODE>, 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 @@
* 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 @@
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
+ * @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
+ * <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
+ * throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the <CODE>createMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
+ * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
+ * will not be registered.
+ * @exception RuntimeErrorException If the <CODE>postRegister</CODE> method
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
+ * <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
+ * throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
+ * and registration succeeded. In such a case, the MBean will be actually
+ * registered even though the <CODE>createMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
+ * also be thrown by <CODE>preRegister</CODE>, 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 @@
* 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 @@
* @exception MBeanRegistrationException The preDeregister
* ((<CODE>MBeanRegistration</CODE> interface) method of the MBean
* has thrown an exception.
+ * @exception RuntimeMBeanException If the <CODE>postDeregister</CODE>
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
+ * <CODE>RuntimeException</CODE>, the <CODE>unregisterMBean<CODE> method
+ * will throw a <CODE>RuntimeMBeanException</CODE>, although the MBean
+ * unregistration succeeded. In such a case, the MBean will be actually
+ * unregistered even though the <CODE>unregisterMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
+ * also be thrown by <CODE>preDeregister</CODE>, in which case the MBean
+ * will remain registered.
+ * @exception RuntimeErrorException If the <CODE>postDeregister</CODE>
+ * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
+ * <CODE>Error</CODE>, the <CODE>unregisterMBean<CODE> method will
+ * throw a <CODE>RuntimeErrorException</CODE>, although the MBean
+ * unregistration succeeded. In such a case, the MBean will be actually
+ * unregistered even though the <CODE>unregisterMBean<CODE> method
+ * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
+ * also be thrown by <CODE>preDeregister</CODE>, in which case the MBean
+ * will remain registered.
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: The object
* name in parameter is null or the MBean you are when trying to
@@ -282,7 +373,7 @@
* 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,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/MBeanServer/PostExceptionTest.java Thu Jul 31 12:41:35 2008 +0200
@@ -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> where;
+ public Case(Throwable t,EnumSet<WHERE> 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> 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> 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> 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> 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> 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> 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> 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> 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> where;
+ private volatile boolean end=false;
+
+ public ExceptionalWombat(Throwable t, EnumSet<WHERE> 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<WHERE> w;
+ public ExceptionallyHackyWombat() {
+ super(t,w);
+ }
+ }
+
+}