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