OpenJDK / jdk / jdk
changeset 1002:1e6a1b77f22a
6730926: Document that create/registerMBean can throw RuntimeMBeanException from postRegister
Reviewed-by: emcmanus
author | dfuchs |
---|---|
date | Thu, 31 Jul 2008 12:41:35 +0200 |
parents | e3d549324eb4 |
children | b2f6b7e00c29 |
files | jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java jdk/src/share/classes/javax/management/MBeanRegistration.java jdk/src/share/classes/javax/management/MBeanServer.java jdk/src/share/classes/javax/management/MBeanServerConnection.java jdk/test/javax/management/MBeanServer/PostExceptionTest.java |
diffstat | 5 files changed, 693 insertions(+), 19 deletions(-) [+] |
line wrap: on
line diff
--- 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); + } + } + +}