PostExceptionTest.java revision 11884:b45c81ca8671
1/*
2 * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 6730926
27 * @summary Check behaviour of MBeanServer when postRegister and postDeregister
28 *          throw exceptions.
29 * @author Daniel Fuchs
30 * @modules java.management
31 * @compile PostExceptionTest.java
32 * @run main PostExceptionTest
33 */
34
35import javax.management.*;
36import java.io.Serializable;
37import java.net.URL;
38import java.util.EnumSet;
39import javax.management.loading.MLet;
40
41public class PostExceptionTest {
42
43    /**
44     * A test case where we instantiate an ExceptionalWombatMBean (or a
45     * subclass of it) which will throw the exception {@code t} from within
46     * the methods indicated by {@code where}
47     */
48    public static class Case {
49        public final Throwable t;
50        public final EnumSet<WHERE> where;
51        public Case(Throwable t,EnumSet<WHERE> where) {
52            this.t=t; this.where=where;
53        }
54    }
55
56    // Various methods to create an instance of Case in a single line
57    // --------------------------------------------------------------
58
59    public static Case caze(Throwable t, WHERE w) {
60        return new Case(t,EnumSet.of(w));
61    }
62    public static Case caze(Throwable t, EnumSet<WHERE> where) {
63        return new Case(t,where);
64    }
65    public static Case caze(Throwable t, WHERE w, WHERE... rest) {
66        return new Case(t,EnumSet.of(w,rest));
67    }
68
69    /**
70     * Here is the list of our test cases:
71     */
72    public static Case[] cases ={
73        caze(new RuntimeException(),WHERE.PREREGISTER),
74        caze(new RuntimeException(),WHERE.POSTREGISTER),
75        caze(new RuntimeException(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
76        caze(new RuntimeException(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
77        caze(new Exception(),WHERE.PREREGISTER),
78        caze(new Exception(),WHERE.POSTREGISTER),
79        caze(new Exception(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
80        caze(new Exception(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
81        caze(new Error(),WHERE.PREREGISTER),
82        caze(new Error(),WHERE.POSTREGISTER),
83        caze(new Error(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
84        caze(new Error(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
85        caze(new RuntimeException(),EnumSet.allOf(WHERE.class)),
86        caze(new Exception(),EnumSet.allOf(WHERE.class)),
87        caze(new Error(),EnumSet.allOf(WHERE.class)),
88    };
89
90    public static void main(String[] args) throws Exception {
91        System.out.println("Test behaviour of MBeanServer when postRegister " +
92                "or postDeregister throw exceptions");
93        MBeanServer mbs = MBeanServerFactory.newMBeanServer();
94        int failures = 0;
95        final ObjectName n = new ObjectName("test:type=Wombat");
96
97        // We're going to test each cases, using each of the 4 createMBean
98        // forms + registerMBean in turn to create the MBean.
99        // Wich method is used to create the MBean is indicated by "how"
100        //
101        for (Case caze:cases) {
102            for (CREATE how : CREATE.values()) {
103                failures+=test(mbs,n,how,caze.t,caze.where);
104            }
105        }
106        if (failures == 0)
107            System.out.println("Test passed");
108        else {
109            System.out.println("TEST FAILED: " + failures + " failure(s)");
110            System.exit(1);
111        }
112    }
113
114    // Execute a test case composed of:
115    // mbs:   The MBeanServer where the MBean will be registered,
116    // name:  The name of that MBean
117    // how:   How will the MBean be created/registered (which MBeanServer
118    //        method)
119    // t:     The exception/error that the MBean will throw
120    // where: In which pre/post register/deregister method the exception/error
121    //        will be thrown
122    //
123    private static int test(MBeanServer mbs, ObjectName name, CREATE how,
124            Throwable t, EnumSet<WHERE> where)
125            throws Exception {
126        System.out.println("-------<"+how+"> / <"+t+"> / "+ where + "-------");
127
128        int failures = 0;
129        ObjectInstance oi = null;
130        Exception reg = null;    // exception thrown by create/register
131        Exception unreg = null;  // exception thrown by unregister
132        try {
133            // Create the MBean
134            oi = how.create(t, where, mbs, name);
135        } catch (Exception xx) {
136            reg=xx;
137        }
138        final ObjectName n = (oi==null)?name:oi.getObjectName();
139        final boolean isRegistered = mbs.isRegistered(n);
140        try {
141            // If the MBean is registered, unregister it
142            if (isRegistered) mbs.unregisterMBean(n);
143        } catch (Exception xxx) {
144            unreg=xxx;
145        }
146        final boolean isUnregistered = !mbs.isRegistered(n);
147        if (!isUnregistered) {
148            // if the MBean is still registered (preDeregister threw an
149            // exception) signify to the MBean that it now should stop
150            // throwing anaything and unregister it.
151            JMX.newMBeanProxy(mbs, n, ExceptionalWombatMBean.class).end();
152            mbs.unregisterMBean(n);
153        }
154
155        // Now analyze the result. If we didn't ask the MBean to throw any
156        // exception then reg should be null.
157        if (where.isEmpty() && reg!=null) {
158            System.out.println("Unexpected registration exception: "+
159                    reg);
160            throw new RuntimeException("Unexpected registration exception: "+
161                    reg,reg);
162        }
163
164        // If we didn't ask the MBean to throw any exception then unreg should
165        // also be null.
166        if (where.isEmpty() && unreg!=null) {
167            System.out.println("Unexpected unregistration exception: "+
168                    unreg);
169            throw new RuntimeException("Unexpected unregistration exception: "+
170                    unreg,unreg);
171        }
172
173        // If we asked the MBean to throw an exception in either of preRegister
174        // or postRegister, then reg should not be null.
175        if ((where.contains(WHERE.PREREGISTER)
176            || where.contains(WHERE.POSTREGISTER))&& reg==null) {
177            System.out.println("Expected registration exception not " +
178                    "thrown by "+where);
179            throw new RuntimeException("Expected registration exception not " +
180                    "thrown by "+where);
181        }
182
183        // If we asked the MBean not to throw any exception in preRegister
184        // then the MBean should have been registered, unregisterMBean should
185        // have been called.
186        // If we asked the MBean to throw an exception in either of preDeregister
187        // or postDeregister, then unreg should not be null.
188        if ((where.contains(WHERE.PREDEREGISTER)
189            || where.contains(WHERE.POSTDEREGISTER))&& unreg==null
190            && !where.contains(WHERE.PREREGISTER)) {
191            System.out.println("Expected unregistration exception not " +
192                    "thrown by "+where);
193            throw new RuntimeException("Expected unregistration exception not " +
194                    "thrown by "+where);
195        }
196
197        // If we asked the MBean to throw an exception in preRegister
198        // then the MBean should not have been registered.
199        if (where.contains(WHERE.PREREGISTER)) {
200            if (isRegistered) {
201                System.out.println("MBean is still registered [" +
202                        where+
203                        "]: "+name+" / "+reg);
204                throw new RuntimeException("MBean is still registered [" +
205                        where+
206                        "]: "+name+" / "+reg,reg);
207            }
208        }
209
210        // If we asked the MBean not to throw an exception in preRegister,
211        // but to throw an exception in postRegister, then the MBean should
212        // have been registered.
213        if (where.contains(WHERE.POSTREGISTER) &&
214                !where.contains(WHERE.PREREGISTER)) {
215            if (!isRegistered) {
216                System.out.println("MBean is already unregistered [" +
217                        where+
218                        "]: "+name+" / "+reg);
219                throw new RuntimeException("MBean is already unregistered [" +
220                        where+
221                        "]: "+name+" / "+reg,reg);
222            }
223        }
224
225        // If we asked the MBean to throw an exception in preRegister,
226        // check that the exception we caught was as expected.
227        //
228        if (where.contains(WHERE.PREREGISTER)) {
229            WHERE.PREREGISTER.check(reg, t);
230        } else if (where.contains(WHERE.POSTREGISTER)) {
231            // If we asked the MBean to throw an exception in postRegister,
232            // check that the exception we caught was as expected.
233            // We don't do this check if we asked the MBean to also throw an
234            // exception in pre register, because postRegister will not have
235            // been called.
236            WHERE.POSTREGISTER.check(reg, t);
237        }
238
239        if (!isRegistered) return failures;
240
241        // The MBean was registered, so unregisterMBean was called. Check
242        // unregisterMBean exceptions...
243        //
244
245        // If we asked the MBean to throw an exception in preDeregister
246        // then the MBean should not have been deregistered.
247        if (where.contains(WHERE.PREDEREGISTER)) {
248            if (isUnregistered) {
249                System.out.println("MBean is already unregistered [" +
250                        where+
251                        "]: "+name+" / "+unreg);
252                throw new RuntimeException("MBean is already unregistered [" +
253                        where+
254                        "]: "+name+" / "+unreg,unreg);
255            }
256        }
257
258        // If we asked the MBean not to throw an exception in preDeregister,
259        // but to throw an exception in postDeregister, then the MBean should
260        // have been deregistered.
261        if (where.contains(WHERE.POSTDEREGISTER) &&
262                !where.contains(WHERE.PREDEREGISTER)) {
263            if (!isUnregistered) {
264                System.out.println("MBean is not unregistered [" +
265                        where+
266                        "]: "+name+" / "+unreg);
267                throw new RuntimeException("MBean is not unregistered [" +
268                        where+
269                        "]: "+name+" / "+unreg,unreg);
270            }
271        }
272
273        // If we asked the MBean to throw an exception in preDeregister,
274        // check that the exception we caught was as expected.
275        //
276        if (where.contains(WHERE.PREDEREGISTER)) {
277            WHERE.PREDEREGISTER.check(unreg, t);
278        } else if (where.contains(WHERE.POSTDEREGISTER)) {
279            // If we asked the MBean to throw an exception in postDeregister,
280            // check that the exception we caught was as expected.
281            // We don't do this check if we asked the MBean to also throw an
282            // exception in pre register, because postRegister will not have
283            // been called.
284            WHERE.POSTDEREGISTER.check(unreg, t);
285        }
286        return failures;
287    }
288
289    /**
290     * This enum lists the 4 methods in MBeanRegistration.
291     */
292    public static enum WHERE {
293
294        PREREGISTER, POSTREGISTER, PREDEREGISTER, POSTDEREGISTER;
295
296        // Checks that an exception thrown by the MBeanServer correspond to
297        // what is expected when an MBean throws an exception in this
298        // MBeanRegistration method ("this" is one of the 4 enum values above)
299        //
300        public void check(Exception thrown, Throwable t)
301                throws Exception {
302           if (t instanceof RuntimeException) {
303               if (!(thrown instanceof RuntimeMBeanException)) {
304                   System.out.println("Expected RuntimeMBeanException, got "+
305                           thrown);
306                   throw new Exception("Expected RuntimeMBeanException, got "+
307                           thrown);
308               }
309           } else if (t instanceof Error) {
310               if (!(thrown instanceof RuntimeErrorException)) {
311                   System.out.println("Expected RuntimeErrorException, got "+
312                           thrown);
313                   throw new Exception("Expected RuntimeErrorException, got "+
314                           thrown);
315               }
316           } else if (t instanceof Exception) {
317               if (EnumSet.of(POSTDEREGISTER,POSTREGISTER).contains(this)) {
318                   if (!(thrown instanceof RuntimeMBeanException)) {
319                       System.out.println("Expected RuntimeMBeanException, got "+
320                           thrown);
321                       throw new Exception("Expected RuntimeMBeanException, got "+
322                           thrown);
323                   }
324                   if (! (thrown.getCause() instanceof RuntimeException)) {
325                       System.out.println("Bad cause: " +
326                               "expected RuntimeException, " +
327                           "got <"+thrown.getCause()+">");
328                       throw new Exception("Bad cause: " +
329                               "expected RuntimeException, " +
330                           "got <"+thrown.getCause()+">");
331                   }
332               }
333               if (EnumSet.of(PREDEREGISTER,PREREGISTER).contains(this)) {
334                   if (!(thrown instanceof MBeanRegistrationException)) {
335                       System.out.println("Expected " +
336                               "MBeanRegistrationException, got "+
337                           thrown);
338                       throw new Exception("Expected " +
339                               "MBeanRegistrationException, got "+
340                           thrown);
341                   }
342                   if (! (thrown.getCause() instanceof Exception)) {
343                       System.out.println("Bad cause: " +
344                               "expected Exception, " +
345                           "got <"+thrown.getCause()+">");
346                       throw new Exception("Bad cause: " +
347                               "expected Exception, " +
348                           "got <"+thrown.getCause()+">");
349                   }
350               }
351           }
352
353        }
354    }
355
356    /**
357     * This enum lists the 5 methods to create and register an
358     * ExceptionalWombat MBean
359     */
360    public static enum CREATE {
361
362        CREATE1() {
363            // Creates an ExceptionalWombat MBean using createMBean form #1
364            public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
365                    MBeanServer server, ObjectName name) throws Exception {
366                ExceptionallyHackyWombat.t = t;
367                ExceptionallyHackyWombat.w = where;
368                return server.createMBean(
369                        ExceptionallyHackyWombat.class.getName(),
370                        name);
371            }
372        },
373        CREATE2() {
374            // Creates an ExceptionalWombat MBean using createMBean form #2
375            public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
376                    MBeanServer server, ObjectName name) throws Exception {
377                ExceptionallyHackyWombat.t = t;
378                ExceptionallyHackyWombat.w = where;
379                final ObjectName loaderName = registerMLet(server);
380                return server.createMBean(
381                        ExceptionallyHackyWombat.class.getName(),
382                        name, loaderName);
383            }
384        },
385        CREATE3() {
386            // Creates an ExceptionalWombat MBean using createMBean form #3
387            public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
388                    MBeanServer server, ObjectName name) throws Exception {
389                final Object[] params = {t, where};
390                final String[] signature = {Throwable.class.getName(),
391                    EnumSet.class.getName()
392                };
393                return server.createMBean(
394                        ExceptionalWombat.class.getName(), name,
395                        params, signature);
396            }
397        },
398        CREATE4() {
399            // Creates an ExceptionalWombat MBean using createMBean form #4
400            public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
401                    MBeanServer server, ObjectName name) throws Exception {
402                final Object[] params = {t, where};
403                final String[] signature = {Throwable.class.getName(),
404                    EnumSet.class.getName()
405                };
406                return server.createMBean(
407                        ExceptionalWombat.class.getName(), name,
408                        registerMLet(server), params, signature);
409            }
410        },
411        REGISTER() {
412            // Creates an ExceptionalWombat MBean using registerMBean
413            public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
414                    MBeanServer server, ObjectName name) throws Exception {
415                final ExceptionalWombat wombat =
416                        new ExceptionalWombat(t, where);
417                return server.registerMBean(wombat, name);
418            }
419        };
420
421        // Creates an ExceptionalWombat MBean using the method denoted by this
422        // Enum value - one of CREATE1, CREATE2, CREATE3, CREATE4, or REGISTER.
423        public abstract ObjectInstance create(Throwable t, EnumSet<WHERE> where,
424                MBeanServer server, ObjectName name) throws Exception;
425
426        // This is a bit of a hack - we use an MLet that delegates to the
427        // System ClassLoader so that we can use createMBean form #2 and #3
428        // while still using the same class loader (system).
429        // This is necessary to make the ExceptionallyHackyWombatMBean work ;-)
430        //
431        public ObjectName registerMLet(MBeanServer server) throws Exception {
432            final ObjectName name = new ObjectName("test:type=MLet");
433            if (server.isRegistered(name)) {
434                return name;
435            }
436            final MLet mlet = new MLet(new URL[0],
437                    ClassLoader.getSystemClassLoader());
438            return server.registerMBean(mlet, name).getObjectName();
439        }
440    }
441
442    /**
443     *A Wombat MBean that can throw exceptions or errors in any of the
444     * MBeanRegistration methods.
445     */
446    public static interface ExceptionalWombatMBean {
447        // Tells the MBean to stop throwing exceptions - we sometime
448        // need to call this at the end of the test so that we can
449        // actually unregister the MBean.
450        public void end();
451    }
452
453    /**
454     *A Wombat MBean that can throw exceptions or errors in any of the
455     * MBeanRegistration methods.
456     */
457    public static class ExceptionalWombat
458            implements ExceptionalWombatMBean, MBeanRegistration {
459
460        private final Throwable throwable;
461        private final EnumSet<WHERE> where;
462        private volatile boolean end=false;
463
464        public ExceptionalWombat(Throwable t, EnumSet<WHERE> where) {
465            this.throwable=t; this.where=where;
466        }
467        private Exception doThrow() {
468            if (throwable instanceof Error)
469                throw (Error)throwable;
470            if (throwable instanceof RuntimeException)
471                throw (RuntimeException)throwable;
472            return (Exception)throwable;
473        }
474        public ObjectName preRegister(MBeanServer server, ObjectName name)
475                throws Exception {
476            if (!end && where.contains(WHERE.PREREGISTER))
477                throw doThrow();
478            return name;
479        }
480
481        public void postRegister(Boolean registrationDone) {
482            if (!end && where.contains(WHERE.POSTREGISTER))
483                throw new RuntimeException(doThrow());
484        }
485
486        public void preDeregister() throws Exception {
487            if (!end && where.contains(WHERE.PREDEREGISTER))
488                throw doThrow();
489        }
490
491        public void postDeregister() {
492            if (!end && where.contains(WHERE.POSTREGISTER))
493                throw new RuntimeException(doThrow());
494        }
495
496        public void end() {
497            this.end=true;
498        }
499    }
500
501    /**
502     * This is a big ugly hack to call createMBean form #1 and #2 - where
503     * the empty constructor is used. Since we still want to supply parameters
504     * to the ExceptionalWombat super class, we temporarily store these
505     * parameter value in a static volatile before calling create MBean.
506     * Of course this only works because our test is sequential and single
507     * threaded, and nobody but our test uses this ExceptionallyHackyWombat.
508     */
509    public static class ExceptionallyHackyWombat extends ExceptionalWombat {
510        public static volatile Throwable  t;
511        public static volatile EnumSet<WHERE> w;
512        public ExceptionallyHackyWombat() {
513            super(t,w);
514        }
515    }
516
517}
518