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