1 /* 2 * Copyright (c) 2005, 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 import java.util.Map; 25 import java.util.HashMap; 26 import java.util.Properties; 27 import java.util.StringTokenizer; 28 import java.lang.reflect.Method; 29 import javax.management.remote.JMXConnectorServerMBean; 30 31 // utility class for MXBean* tests coming from JMX Tonga test suite 32 class Utils { 33 34 private static final String SERVER_SIDE_NAME = "-server"; 35 private static final String CLIENT_SIDE_NAME = "-client"; 36 37 // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property 38 private static final String DEBUG_HEADER = "[debug] "; 39 40 // DEBUG levels 41 private static int selectedDebugLevel = 0; 42 static final int DEBUG_STANDARD = 1; 43 static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests 44 static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE; 45 parseDebugProperties()46 static void parseDebugProperties() { 47 int level = 0; 48 Properties p = System.getProperties(); 49 50 // get selected levels 51 if (p.getProperty("DEBUG_STANDARD") != null) { 52 level |= DEBUG_STANDARD; 53 } 54 55 if (p.getProperty("DEBUG_VERBOSE") != null) { 56 level |= DEBUG_VERBOSE; 57 } 58 59 if (p.getProperty("DEBUG_ALL") != null) { 60 level |= DEBUG_ALL; 61 } 62 63 selectedDebugLevel = level; 64 } 65 66 /** 67 * Reproduces the original parsing and collection of test parameters 68 * from the DTonga JMX test suite. 69 * 70 * Collects passed args and returns them in a map(argname, value) structure, 71 * which will be then propagated as necessary to various called methods. 72 */ parseParameters(String args[])73 static Map<String, Object> parseParameters(String args[]) 74 throws Exception { 75 Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start"); 76 HashMap<String, Object> map = new HashMap<>(); 77 78 for ( int i = 0; i < args.length; i++ ) { 79 if ( args[i].trim().startsWith("-") ) { 80 if ((i+1) < args.length && !args[i+1].startsWith("-") ) { 81 Utils.debug(DEBUG_STANDARD, 82 "TestRoot::parseParameters: added in map = " + 83 args[i] + 84 " with value " + 85 args[i+1]) ; 86 map.put(args[i].trim(), args[i+1].trim()) ; 87 } else if ((i+1) < args.length && args[i+1].startsWith("-") || 88 (i+1) == args.length ) { 89 Utils.debug(DEBUG_STANDARD, 90 "TestRoot::parseParameters: added in map = " + 91 args[i] + 92 " with null value") ; 93 map.put(args[i].trim(), null) ; 94 } else { 95 System.out.println( 96 "TestRoot::parseParameters: (WARNING) not added in map = " + 97 args[i]) ; 98 } 99 } 100 } 101 102 Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ; 103 return map ; 104 } 105 106 // Parse server parameters and put them in passed serverMap parseServerParameters(String args[], String serverSideName, Map<String, Object> serverMap )107 static int parseServerParameters(String args[], 108 String serverSideName, 109 Map<String, Object> serverMap ) 110 throws Exception { 111 Utils.debug(Utils.DEBUG_STANDARD, 112 serverSideName + "::parseServerParameters: Start"); 113 114 int nextIndex = 0; 115 boolean seenServerFlag = false; 116 117 for ( int i = 0; i < args.length; i++ ) { 118 // Case of reaching "-server" flag parameter 119 if (args[i].equals(SERVER_SIDE_NAME)) { 120 if (!seenServerFlag) { 121 seenServerFlag = true; 122 continue; 123 } else { 124 // Already parsing server params, invalid params list 125 Utils.debug(Utils.DEBUG_STANDARD, 126 serverSideName + "::parseParameters: Invalid " + 127 args[i] + " parameter detected in " + 128 SERVER_SIDE_NAME + " parameters list"); 129 nextIndex = -1; 130 throw new RuntimeException("Invalid Parameter list"); 131 } 132 } 133 134 // Case of reaching "-client" flag parameter 135 if (args[i].equals(CLIENT_SIDE_NAME)) { 136 // While parsing server parameters, then parsing is done. 137 Utils.debug(Utils.DEBUG_STANDARD, 138 serverSideName + "::parseServerParameters: Parsing of " + 139 SERVER_SIDE_NAME + " parameters done."); 140 return i; 141 } 142 143 i = parseParamAtIndex(args, i, serverMap); 144 nextIndex = i; 145 } 146 147 Utils.debug(Utils.DEBUG_STANDARD, 148 serverSideName + "::parseServerParameters: Parsing of parameters done"); 149 150 return nextIndex; 151 } 152 153 // Parse client parameters and put them in passed clientMap parseClientParameters(String args[], String clientSideName, Map<String, Object> clientMap )154 static void parseClientParameters(String args[], 155 String clientSideName, 156 Map<String, Object> clientMap ) 157 throws Exception { 158 159 Utils.debug(Utils.DEBUG_STANDARD, 160 clientSideName + "::parseClientParameters: Start"); 161 162 boolean seenClientFlag = false; 163 164 for ( int i = 0; i < args.length; i++ ) { 165 // Case of reaching "-client" flag parameter 166 if (args[i].equals(CLIENT_SIDE_NAME)) { 167 if (!seenClientFlag) { 168 seenClientFlag = true; 169 continue; 170 } else { 171 // Already parsing client params, invalid params list 172 Utils.debug(Utils.DEBUG_STANDARD, 173 clientSideName + "::parseClientParameters: Invalid " + 174 CLIENT_SIDE_NAME + " parameter detected in " + 175 CLIENT_SIDE_NAME + " parameters list."); 176 throw new RuntimeException("Invalid parameter in " + 177 clientSideName + " parameter list"); 178 } 179 } 180 181 // Case of reaching "-server" flag parameter 182 if (args[i].equals(SERVER_SIDE_NAME)) { 183 // While parsing client parameters, invalid parameter list. 184 Utils.debug(Utils.DEBUG_STANDARD, 185 clientSideName + "::parseClientParameters: Invalid " + 186 SERVER_SIDE_NAME + " parameter inside " + 187 CLIENT_SIDE_NAME + " parameters list."); 188 throw new RuntimeException("Invalid parameter in " + 189 clientSideName + " parameter list"); 190 } 191 192 i = parseParamAtIndex(args, i, clientMap); 193 } 194 195 Utils.debug(Utils.DEBUG_STANDARD, 196 clientSideName + "::parseClientParameters: Parsing of parameters done."); 197 } 198 199 // Add param found at index to passed map 200 // We only accept either "-param value" or "-param" form. 201 // The "value" form is invalid but just ignored. parseParamAtIndex(String args[], int index, Map<String, Object> map)202 private static int parseParamAtIndex(String args[], 203 int index, 204 Map<String, Object> map) { 205 206 if (args[index].trim().startsWith("-") ) { 207 // Case of a "-param value" form 208 if ((index+1) < args.length && !args[index+1].startsWith("-") ) { 209 Utils.debug(Utils.DEBUG_STANDARD, 210 "TestRoot::parseParamAtIndex: added in map = " 211 + args[index] 212 + " with value " 213 + args[index+1]) ; 214 // adding ("param", value) to the passed map 215 map.put(args[index].trim(), args[index+1].trim()) ; 216 // value should not be parsed a second time 217 return index+1; 218 } 219 // Case of a "-param" form (flag parameter) 220 else if (((index+1) < args.length && args[index+1].startsWith("-")) || 221 (index+1) == args.length ) { 222 Utils.debug(Utils.DEBUG_STANDARD, 223 "TestRoot::parseParamAtIndex: added in map = " 224 + args[index] 225 + " with null value") ; 226 // adding ("param", null) to passed map 227 map.put(args[index].trim(), null) ; 228 } 229 } else { 230 // Unsupported "value" alone parameter 231 Utils.debug(Utils.DEBUG_STANDARD, 232 "TestRoot::parseParamAtIndex: invalid " + 233 " value-alone \"" + args[index] + "\" parameter." + 234 " Parameter ignored."); 235 } 236 237 return index; 238 } 239 240 /** 241 * This method is to be used in all tests to print anything 242 * that is temporary. 243 * Printing is done only when debug is activated by the property DEBUG. 244 * Printing depends also on the DEBUG_LEVEL property. 245 * Here it encapsulates a System.out.println. 246 */ debug(int level, String line)247 static void debug(int level, String line) { 248 if ((selectedDebugLevel & level) != 0) { 249 System.out.println(DEBUG_HEADER + line); 250 } 251 } 252 253 /** 254 * Do print stack trace when withStack is true. 255 * Does try to call getTargetException() and getTargetError() then 256 * print embedded stacks in the case of an Exception wrapping 257 * another Exception or an Error. Recurse until no more wrapping 258 * is found. 259 */ printThrowable(Throwable theThro, boolean withStack)260 static void printThrowable(Throwable theThro, boolean withStack) { 261 try { 262 if (withStack) { 263 theThro.printStackTrace(System.out); 264 } 265 if (theThro instanceof Exception) { 266 Exception t = (Exception) theThro; 267 Method target = null; 268 String blank = " "; 269 try { 270 target = t.getClass().getMethod("getTargetException", 271 (java.lang.Class<?>[]) null); 272 } catch (Exception ee) { 273 // OK: getTargetException method could be there or not 274 } 275 System.out.println(blank + t.getClass() + "==>" + t.getMessage()); 276 while (target != null) { 277 try { 278 t = (Exception) target.invoke(t, 279 (java.lang.Object[]) null); 280 } catch (Exception ee) { 281 t = null; 282 } 283 try { 284 if (t != null) { 285 blank = blank + " "; 286 System.out.println(blank + t.getClass() + "==>" + 287 t.getMessage()); 288 try { 289 target = 290 t.getClass().getMethod("getTargetException", 291 (java.lang.Class<?>[]) null); 292 } catch (Exception ee) { 293 // OK: getTargetException method could be there or not } 294 } 295 } else { 296 target = null; 297 } 298 } catch (Exception ee) { 299 target = null; 300 } 301 } 302 303 // We may have exceptions wrapping an Error then it is 304 // getTargetError that is likely to be called 305 try { 306 target = ((Exception) theThro).getClass().getMethod("getTargetError", 307 (java.lang.Class<?>[]) null); 308 } catch (Exception ee) { 309 // OK: getTargetError method could be there or not 310 } 311 Throwable err = theThro; 312 while (target != null) { 313 try { 314 err = (Error) target.invoke(err, 315 (java.lang.Object[]) null); 316 } catch (Exception ee) { 317 err = null; 318 } 319 try { 320 if (err != null) { 321 blank = blank + " "; 322 System.out.println(blank + err.getClass() + "==>" + 323 err.getMessage()); 324 if (withStack) { 325 err.printStackTrace(System.out); 326 } 327 try { 328 target = err.getClass().getMethod("getTargetError", 329 (java.lang.Class<?>[]) null); 330 } catch (Exception ee) { 331 // OK: getTargetError method could be there or not 332 } 333 } else { 334 target = null; 335 } 336 } catch (Exception ee) { 337 target = null; 338 } 339 } 340 } else { 341 System.out.println("Throwable is : " + theThro); 342 } 343 } catch (Throwable x) { 344 System.out.println("Exception : raised in printException : " + x); 345 } 346 } 347 348 /** 349 * Wait up to maxTimeInSeconds second(s) the given JMX connector server 350 * comes up (which means isActive returns true). 351 * If it fails to do so we throw a RunTime exception. 352 */ waitReady(JMXConnectorServerMBean server, int maxTimeInSeconds)353 static void waitReady(JMXConnectorServerMBean server, 354 int maxTimeInSeconds) throws Exception { 355 int elapsed = 0; 356 357 while (!server.isActive() && elapsed < maxTimeInSeconds) { 358 Thread.sleep(1000); 359 elapsed++; 360 } 361 362 if (server.isActive()) { 363 String message = "Utils::waitReady: JMX connector server came up"; 364 if ( elapsed == 0) { 365 message += " immediately"; 366 } else { 367 message += " after " + elapsed + " seconds"; 368 } 369 message += " [" + server.getAddress() + "]"; 370 Utils.debug(DEBUG_STANDARD, message); 371 } else { 372 String message = "Utils::waitReady: (ERROR) JMX connector" + 373 " server didn't come up after " + elapsed + " seconds [" + 374 server.getAddress() + "]"; 375 System.out.println(message); 376 throw new RuntimeException(message); 377 } 378 } 379 380 /** 381 * This method is used to compare the specified Throwable and possibly 382 * the derived causes to the specified String argument. 383 * The expected String argument format is : 384 * throwable_1;throwable_2;...;throwable_N 385 * where throwable_i can be : 386 * - either a throwable class name 387 * - or the "*" character meaning several unknown throwable class names 388 * This character must be followed by a throwable class name 389 */ compareThrowable( Throwable t, String expectedThrowable)390 static boolean compareThrowable( 391 Throwable t, 392 String expectedThrowable) { 393 394 // First parse the expectedThrowable String 395 StringTokenizer tokenizer = new StringTokenizer(expectedThrowable, ";"); 396 String token = null; 397 398 try { 399 while (tokenizer.hasMoreTokens()) { 400 token = tokenizer.nextToken(); 401 if (!token.equals("*")) { 402 if (!Class.forName(token).isInstance(t)) { 403 return false; 404 } 405 } else { 406 token = tokenizer.nextToken(); 407 while (!Class.forName(token).isInstance(t)) { 408 t = t.getCause(); 409 if (t == null) { 410 return false; 411 } 412 } 413 } 414 t = t.getCause(); 415 } 416 } catch (ClassNotFoundException cnfe) { 417 String msg = "Expected throwable class(es) " + expectedThrowable + 418 " cannot be located"; 419 System.out.println(msg); 420 throw new IllegalArgumentException(msg); 421 } 422 return true; 423 } 424 } 425