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 35 import javax.management.*; 36 import java.io.Serializable; 37 import java.net.URL; 38 import java.util.EnumSet; 39 import javax.management.loading.MLet; 40 41 public 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 }