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 8058865 27 * @summary Checks that exceptions are correctly wired (compared to reference). 28 * @author Olivier Lagneau 29 * 30 * @modules java.management.rmi 31 * 32 * @run main/othervm/timeout=300 -DDEBUG_STANDARD ExceptionTest 33 */ 34 35 import java.util.Map; 36 import java.util.HashMap; 37 import java.util.Properties; 38 import java.lang.reflect.Method; 39 40 import java.lang.management.ManagementFactory; 41 import javax.management.ObjectName; 42 import javax.management.MBeanServer; 43 import javax.management.MBeanServerConnection; 44 import javax.management.remote.JMXConnector; 45 import javax.management.remote.JMXConnectorFactory; 46 import javax.management.remote.JMXConnectorServer; 47 import javax.management.remote.JMXConnectorServerFactory; 48 import javax.management.remote.JMXServiceURL; 49 50 51 public class ExceptionTest { 52 53 /* 54 * First Debug properties and arguments are collect in expected 55 * map (argName, value) format, then calls original test's run method. 56 */ 57 public static void main(String args[]) throws Exception { 58 59 System.out.println("================================================="); 60 61 // Parses parameters 62 Utils.parseDebugProperties(); 63 Map<String, Object> map = Utils.parseParameters(args) ; 64 65 // Run test 66 ExceptionTest test = new ExceptionTest(); 67 test.run(map); 68 69 } 70 71 public void run(Map<String, Object> args) { 72 73 System.out.println("ExceptionTest::run: Start"); 74 int errorCount = 0; 75 76 JMXConnectorServer cs = null; 77 JMXConnector cc = null; 78 79 try { 80 // JMX MbeanServer used inside single VM as if remote. 81 MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 82 83 JMXServiceURL url = new JMXServiceURL("rmi", null, 0); 84 cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); 85 cs.start(); 86 87 JMXServiceURL addr = cs.getAddress(); 88 cc = JMXConnectorFactory.connect(addr); 89 MBeanServerConnection mbsc = cc.getMBeanServerConnection(); 90 91 // ---- 92 ObjectName objName = 93 new ObjectName(ExceptionThrower.EXCEPTION_THROWER_NAME); 94 System.out.println("ExceptionTest::run: Create and register MBean " + objName); 95 mbsc.createMBean("ExceptionThrower", objName); 96 System.out.println("---- OK\n"); 97 98 // ---- 99 System.out.println("ExceptionTest::run: Ask for exception(s)"); 100 Object[] throwExceptionParam = new Object[1]; 101 String[] throwExceptionSig = new String[]{"int"}; 102 103 for (int i = 0; i < ExceptionFactory.exceptions.size(); i++) { 104 throwExceptionParam[0] = new Integer(i); 105 106 Exception ex = 107 (Exception)mbsc.invoke(objName, 108 "throwException", throwExceptionParam, throwExceptionSig); 109 110 if ( ! matches(ex, ExceptionFactory.exceptions.get(i)) ) { 111 errorCount++; 112 System.out.println("ExceptionTest::run: (ERROR) Received \n[" 113 + ex + "]\nin place of\n[" 114 + ExceptionFactory.exceptions.get(i) + "]"); 115 } else { 116 System.out.println("OK [" + ex + "]"); 117 } 118 } 119 120 System.out.println("---- DONE\n"); 121 122 } catch (Exception e) { 123 Utils.printThrowable(e, true); 124 throw new RuntimeException(); 125 } finally { 126 try { 127 // Close JMX Connector Client 128 cc.close(); 129 // Stop connertor server 130 cs.stop(); 131 132 } catch (Exception e) { 133 Utils.printThrowable(e, true); 134 throw new RuntimeException( 135 "Unable to either close connector client or stop connector server"); 136 } 137 } 138 139 if (errorCount == 0) { 140 System.out.println("ExceptionTest::run: Done without any error"); 141 } else { 142 System.out.println("ExceptionTest::run: Done with " + errorCount 143 + " error(s)"); 144 throw new RuntimeException("errorCount = " + errorCount); 145 } 146 147 System.out.println("ExceptionTest::run: Done"); 148 } 149 150 // Check both Exception are identical. 151 // That means: 152 // - none is null. 153 // - they are of the same Class. 154 // - if their respective messages aren't null they're equal. 155 // - if the message of one is null the message of the other is null too. 156 private boolean matches(Exception ex, Exception refex) { 157 if ( ex == null || refex == null ) { 158 System.out.println("(ERROR) Called with one or more null parameter; check " 159 + ex + " against " + refex); 160 return false; 161 } 162 163 String exClass = ex.getClass().getName(); 164 String refexClass = refex.getClass().getName(); 165 166 if ( ! exClass.equals(refexClass) ) { 167 System.out.println("(ERROR) Class names don't match; check [" 168 + exClass + "] against [" + refexClass + "]"); 169 return false; 170 } 171 172 String exMes = ex.getMessage(); 173 String refexMes = refex.getMessage(); 174 175 if ( exMes != null && refexMes != null ) { 176 if ( ! exMes.equals(refexMes) ) { 177 System.out.println("(ERROR) Non null messages don't match; check [" 178 + exMes + "] against [" + refexMes + "]"); 179 return false; 180 } 181 } else if ( (exMes == null && refexMes != null) 182 || (exMes != null && refexMes == null) ) { 183 System.out.println("(ERROR) Messages don't match; check [" + exMes 184 + "] against [" + refexMes + "]"); 185 } 186 187 return true; 188 } 189 190 // Utility inner class coming from JMX Tonga test suite. 191 // Also used by ExceptionFactory. 192 static class Utils { 193 194 // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property 195 static final String DEBUG_HEADER = "[debug] "; 196 197 // DEBUG levels 198 static int selectedDebugLevel = 0; 199 static final int DEBUG_STANDARD = 1; 200 static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests 201 static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE; 202 203 static void parseDebugProperties() { 204 int level = 0; 205 Properties p = System.getProperties(); 206 207 // get selected levels 208 if (p.getProperty("DEBUG_STANDARD") != null) { 209 level |= DEBUG_STANDARD; 210 } 211 212 if (p.getProperty("DEBUG_VERBOSE") != null) { 213 level |= DEBUG_VERBOSE; 214 } 215 216 if (p.getProperty("DEBUG_ALL") != null) { 217 level |= DEBUG_ALL; 218 } 219 220 selectedDebugLevel = level; 221 } 222 223 /** 224 * Reproduces the original parsing and collection of test parameters 225 * from the DTonga JMX test suite. 226 * 227 * Collects passed args and returns them in a map(argname, value) structure, 228 * which will be then propagated as necessary to various called methods. 229 */ 230 static Map<String, Object> parseParameters(String args[]) 231 throws Exception { 232 debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start"); 233 HashMap<String, Object> map = new HashMap<>(); 234 235 for ( int i = 0; i < args.length; i++ ) { 236 if ( args[i].trim().startsWith("-") ) { 237 if ((i+1) < args.length && !args[i+1].startsWith("-") ) { 238 debug(DEBUG_STANDARD, 239 "TestRoot::parseParameters: added in map = " + 240 args[i] + 241 " with value " + 242 args[i+1]) ; 243 map.put(args[i].trim(), args[i+1].trim()) ; 244 } else if ((i+1) < args.length && args[i+1].startsWith("-") || 245 (i+1) == args.length ) { 246 debug(DEBUG_STANDARD, 247 "TestRoot::parseParameters: added in map = " + 248 args[i] + 249 " with null value") ; 250 map.put(args[i].trim(), null) ; 251 } else { 252 System.out.println( 253 "TestRoot::parseParameters: (WARNING) not added in map = " + 254 args[i]) ; 255 } 256 } 257 } 258 259 debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ; 260 return map ; 261 } 262 263 /** 264 * This method is to be used in all tests to print anything 265 * that is temporary. 266 * Printing is done only when debug is activated by the property DEBUG. 267 * Printing depends also on the DEBUG_LEVEL property. 268 * Here it encapsulates a System.out.println. 269 */ 270 static void debug(int level, String line) { 271 if ((selectedDebugLevel & level) != 0) { 272 System.out.println(DEBUG_HEADER + line); 273 } 274 } 275 276 /** 277 * Do print stack trace when withStack is true. 278 * Does try to call getTargetException() and getTargetError() then 279 * print embedded stacks in the case of an Exception wrapping 280 * another Exception or an Error. Recurse until no more wrapping 281 * is found. 282 */ 283 static void printThrowable(Throwable theThro, boolean withStack) { 284 try { 285 if (withStack) { 286 theThro.printStackTrace(System.out); 287 } 288 if (theThro instanceof Exception) { 289 Exception t = (Exception) theThro; 290 Method target = null; 291 String blank = " "; 292 try { 293 target = t.getClass().getMethod("getTargetException", 294 (java.lang.Class<?>[]) null); 295 } catch (Exception ee) { 296 // OK: getTargetException method could be there or not 297 } 298 System.out.println(blank + t.getClass() + "==>" + t.getMessage()); 299 while (target != null) { 300 try { 301 t = (Exception) target.invoke(t, 302 (java.lang.Object[]) null); 303 } catch (Exception ee) { 304 t = null; 305 } 306 try { 307 if (t != null) { 308 blank = blank + " "; 309 System.out.println(blank + t.getClass() + "==>" + 310 t.getMessage()); 311 try { 312 target = 313 t.getClass().getMethod("getTargetException", 314 (java.lang.Class<?>[]) null); 315 } catch (Exception ee) { 316 // OK: getTargetException method could be there or not } 317 } 318 } else { 319 target = null; 320 } 321 } catch (Exception ee) { 322 target = null; 323 } 324 } 325 326 // We may have exceptions wrapping an Error then it is 327 // getTargetError that is likely to be called 328 try { 329 target = ((Exception) theThro).getClass().getMethod("getTargetError", 330 (java.lang.Class<?>[]) null); 331 } catch (Exception ee) { 332 // OK: getTargetError method could be there or not 333 } 334 Throwable err = theThro; 335 while (target != null) { 336 try { 337 err = (Error) target.invoke(err, 338 (java.lang.Object[]) null); 339 } catch (Exception ee) { 340 err = null; 341 } 342 try { 343 if (err != null) { 344 blank = blank + " "; 345 System.out.println(blank + err.getClass() + "==>" + 346 err.getMessage()); 347 if (withStack) { 348 err.printStackTrace(System.out); 349 } 350 try { 351 target = err.getClass().getMethod("getTargetError", 352 (java.lang.Class<?>[]) null); 353 } catch (Exception ee) { 354 // OK: getTargetError method could be there or not 355 } 356 } else { 357 target = null; 358 } 359 } catch (Exception ee) { 360 target = null; 361 } 362 } 363 } else { 364 System.out.println("Throwable is : " + theThro); 365 } 366 } catch (Throwable x) { 367 System.out.println("Exception : raised in printException : " + x); 368 } 369 } 370 } 371 372 }