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