1 /*
2  * Copyright (c) 2003, 2018, 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 package nsk.monitoring.share;
25 
26 import nsk.share.log.Log;
27 import nsk.share.TestBug;
28 import nsk.share.ArgumentParser;
29 import java.lang.management.*;
30 
31 /**
32  * Parser for JSR-174 test's arguments.
33  * <p>
34  * <code>ArgumentHandler</code> handles specific command line arguments
35  * related to way of execution of a test in addition to general arguments
36  * recognized by {@link ArgumentParser <code>ArgumentParser</code>}.
37  * <p>
38  * Following is the list of specific options for <code>ArgumentHandler</code>:
39  * <ul>
40  * <li><code>-testMode="<i>value</i>"</code>, where <i>value</i> may take
41  *      one of the following values: <code>directly</code> -- to call methods in
42  *      the MBean directly within the same JVM, <code>server</code> -- to call
43  *      methods through MBeanServer, <code>proxy</code> -- to call methods
44  *      through MBean proxy (not yet implemented).
45  * <li><code>-MBeanServer="<i>value</i>"</code>, where <i>value</i> may take
46  *      one of the following values: <code>default</code> -- to execute test for
47  *      default JMX implementation of MBeanServer or <code>custom</code> -- for
48  *      implementation provided by NSK J2SE SQE Team.
49  * <li><code>-loadableClassCount=<i>value</i></code>, where <i>value</i> defines
50  *      amount of loadable classes. Default values is <code>100</code>.
51  * <li><code>-loadersCount=<i>value</i></code>, where <i>value</i> defines
52  *      amount of class loaders. Default values is <code>100</code>.
53  * <li><code>-singleClassloaderClass</code> specifies whether class loaders are
54  *      instances of the same class.
55  * <li><code>-memory="<i>value</i>"</code>, where <i>value</i> may take
56  *      one of the following values: <code>heap</code> -- to test heap memory,
57  *      <code>nonheap</code> to test nonheap memory, <code>mixed</code> -- to
58  *      test both heap and nonheap memory.
59  * <li><code>-invocationType="<i>value</i>"</code>, where <i>value</i> may take
60  *      one of the following values: <code>java</code> -- to start java threads,
61  *      <code>native</code> -- to start native threads, <code>mixed</code> -- to
62  *      both java and native threads.
63  * <li><code>-monitoring="<i>value</i>"</code>, where <i>value</i> may take
64  *      one of the following values: <code>polling</code> -- to start polling
65  *      mechanism of monitoring, <code>notification</code> -- to start
66  *      notification mechanism of monitoring.
67  * <li><code>-threshold="<i>value</i>"</code>, where <i>value</i> may take
68  *      one of the following values: <code>usage</code> -- to test usage
69  *      thresholds, <code>collection</code> -- to test collection usage
70  *      thresholds.
71  * <li><code>-depth=<i>value</i></code>, where <i>value</i> defines
72  *      depth of recursion. Default values is <code>1</code>.
73  * <li><code>-threadCount=<i>value</i></code>, where <i>value</i> defines
74  *      number of threads to start. Default values is <code>1</code>.
75  * <li><code>-timeout=<i>value</i></code>, where <i>value</i> defines
76  *      number of minutes to run the test.
77  * </ul>
78  * <p>
79  * See also list of basic options recognized by <code>ArgumentParser</code>.
80  * <p>
81  * See also comments to <code>ArgumentParser</code> how to work with
82  * command line arguments and options.
83  *
84  * @see ArgumentParser
85  */
86 public class ArgumentHandler extends ArgumentParser {
87     static final String TEST_MODE = "testMode";
88     static final String DIRECTLY_MODE = "directly";
89     static final String SERVER_MODE = "server";
90     static final String PROXY_MODE = "proxy";
91 
92     static final String SERVER_TYPE = "MBeanServer";
93     static final String DEFAULT_TYPE = "default";
94     static final String CUSTOM_TYPE = "custom";
95 
96     static final String LOADABLE_CLASSES_COUNT = "loadableClassCount";
97     static final String LOADERS_COUNT = "loadersCount";
98     static final String SINGLE_CLASSLOADER_CLASS = "singleClassloaderClass";
99 
100     static final String MEMORY_TYPE = "memory";
101     static final String MT_HEAP = "heap";
102     static final String MT_NONHEAP = "nonheap";
103     static final String MT_MIXED = "mixed";
104 
105     static final String INVOCATION_TYPE = "invocationType";
106     static final String JAVA_TYPE = "java";
107     static final String NATIVE_TYPE = "native";
108     static final String MIXED_TYPE = "mixed";
109 
110     static final String MONITORING = "monitoring";
111     static final String MON_POLLING = "polling";
112     static final String MON_NOTIF = "notification";
113 
114     static final String THRESHOLD = "threshold";
115     static final String TH_USAGE = "usage";
116     static final String TH_COLLECTION = "collection";
117 
118     static final String THREAD_DEPTH = "depth";
119     static final String THREAD_COUNT = "threadCount";
120     static final String TIMEOUT = "timeout";
121 
122     static final String SCENARIO_TYPE = "scenarioType";
123 
124     static final String ITERATIONS = "iterations";
125 
126     /**
127      * Keep a copy of raw command-line arguments and parse them;
128      * but throw an exception on parsing error.
129      *
130      * @param  args  Array of the raw command-line arguments.
131      *
132      * @throws  BadOption  If unknown option or illegal
133      *                     option value found
134      *
135      * @see ArgumentParser
136      */
ArgumentHandler(String args[])137     public ArgumentHandler(String args[]) {
138         super(args);
139     }
140 
141     /**
142      * Returns the test mode.
143      * <p>
144      * To access the metrics directly, <code>testMode</code> option should
145      * be defined in command line <code>-testMode="directly"</code>.
146      * To access the metrics via MBeanServer, <code>"server"</code> should be
147      * assigned to <code>-testMode="directly"</code>.
148      * <p>
149      * If <code>testMode</code> is not defined by command line, a test is
150      * executed in <code>directly</code> mode.
151      *
152      * @return name of test mode.
153      *
154      */
getTestMode()155     public String getTestMode() {
156         return options.getProperty(TEST_MODE, DIRECTLY_MODE);
157     }
158 
159     /**
160      * Returns a type of MBean server if any.
161      * Two kinds of MBean servers are allowed: default and custom servers.
162      * Default server is an implementation of {@link
163      * javax.management.MBeanServer <tt>javax.management.MBeanServer</tt>}
164      * interface provided by JMX. Custom server is an implementation provided
165      * by NSK J2SE SQE Team. Server type is defined by <tt>MBeanServer</tt>
166      * key in command line <code>-MBeanServer="default"</code> or
167      * <code>-MBeanServer="custom"</code>
168      *
169      * @return <i>MBeanServer</i> server type.
170      *
171      */
getServerType()172     public String getServerType() {
173         return options.getProperty(SERVER_TYPE, DEFAULT_TYPE);
174     }
175 
176     /**
177      * Returns <i>true</i> if default implementation is used.
178      *
179      * @return <i>true</i> if default implementation is used.
180      *
181      * @see #getServerType()
182      */
isDefaultServer()183     public boolean isDefaultServer() {
184         return getServerType().equals(DEFAULT_TYPE);
185     }
186 
187     /**
188      * Returns amount of class loaders.
189      *
190      * @return <i>loadersCount</i> as an integer value
191      */
getLoadersCount()192     public int getLoadersCount() {
193         String val = options.getProperty(LOADERS_COUNT, "100");
194         int number;
195         try {
196             number = Integer.parseInt(val);
197         } catch (NumberFormatException e) {
198             throw new TestBug("Not integer value of \"" + LOADERS_COUNT
199                                     + "\" argument: " + val);
200         }
201         return number;
202     }
203 
204     /**
205      * Returns <i>true</i> if class loaders, which perform class loading, are
206      * instances of the same class. If <code>-singleClassloaderClass</code> key
207      * is not set in command line options, then <i>false</i> is returned.
208      *
209      * @return if class loaders are instances of the same class.
210      *
211      */
singleClassloaderClass()212     public boolean singleClassloaderClass() {
213         return options.getProperty(SINGLE_CLASSLOADER_CLASS) != null;
214     }
215 
216     /**
217      * Returns amount of loadable classes. If <code>-loadableClassesCount</code>
218      * key is not set with command line, <code>100</code> is returned.
219      *
220      * @return <i>loadableClassesCount</i> as an integer value
221      *
222      * @throws TestBug <i>loadableClassesCount</i> is non-numeric value.
223      *
224      */
getLoadableClassesCount()225     public int getLoadableClassesCount() {
226         String val = options.getProperty(LOADABLE_CLASSES_COUNT, "1");
227         int number;
228         try {
229             number = Integer.parseInt(val);
230         } catch (NumberFormatException e) {
231             throw new TestBug("Not integer value of \"" + LOADABLE_CLASSES_COUNT
232                                     + "\" argument: " + val);
233         }
234         return number;
235     }
236 
237     /**
238      * Returns invocation type.
239      *
240      * @return <i>invocationType</i> value
241      *
242      */
getInvocationType()243     public String getInvocationType() {
244         return options.getProperty(INVOCATION_TYPE, JAVA_TYPE);
245     }
246 
247     /**
248      * Returns tested memory type.
249      *
250      * @return <i>memory</i> value
251      *
252      */
getTestedMemory()253     public String getTestedMemory() {
254         return options.getProperty(MEMORY_TYPE, MT_HEAP);
255     }
256 
257     /**
258      * Returns timeout.
259      *
260      * @return <i>timeout</i> value
261      *
262      */
getTimeout()263     public int getTimeout() {
264         String value = options.getProperty(TIMEOUT, "30");
265 
266         try {
267             return Integer.parseInt(value);
268         } catch (NumberFormatException e) {
269             throw new TestBug("Not integer value of \"" + TIMEOUT
270                             + "\" argument: " + value);
271         }
272     }
273 
274     /**
275      * Returns recursion depth.
276      *
277      * @return <i>depth</i> value
278      *
279      */
getThreadDepth()280     public int getThreadDepth() {
281         String value = options.getProperty(THREAD_DEPTH, "1");
282 
283         try {
284             return Integer.parseInt(value);
285         } catch (NumberFormatException e) {
286             throw new TestBug("Not integer value of \"" + THREAD_DEPTH
287                             + "\" argument: " + value);
288         }
289     }
290 
291     /**
292      * Returns number of threads.
293      *
294      * @return <i>threadCount</i> value
295      *
296      */
getThreadCount()297     public int getThreadCount() {
298         String value = options.getProperty(THREAD_COUNT, "1");
299 
300         try {
301             return Integer.parseInt(value);
302         } catch (NumberFormatException e) {
303             throw new TestBug("Not integer value of \"" + THREAD_COUNT
304                             + "\" argument: " + value);
305         }
306     }
307 
308     /**
309      * Returns type of monitoring.
310      *
311      * @return <i>monitoring</i> value
312      *
313      */
getMonitoring()314     public String getMonitoring() {
315         return options.getProperty(MONITORING, MON_NOTIF);
316     }
317 
318     /**
319      * Returns type of threshold.
320      *
321      * @return <i>threshold</i> value
322      *
323      */
getThreshold()324     public String getThreshold() {
325         return options.getProperty(THRESHOLD, TH_USAGE);
326     }
327 
328     /**
329      * Returns thread type to create.
330      */
getScenarioType()331     public String getScenarioType() {
332             return options.getProperty(SCENARIO_TYPE, "running");
333     }
334 
getIterations()335     public int getIterations() {
336             return Integer.parseInt(options.getProperty(ITERATIONS, "3"));
337     }
338 
339     /**
340      * Checks if an option is allowed and has proper value.
341      * This method is invoked by <code>parseArguments()</code>
342      *
343      * @param option option name
344      * @param value string representation of value
345      *                      (could be an empty string too)
346      *              null if this option has no value
347      * @return <i>true</i> if option is allowed and has proper value
348      *         <i>false</i> if otion is not admissible
349      *
350      * @throws <i>BadOption</i> if option has an illegal value
351      *
352      * @see nsk.share.ArgumentParser#parseArguments
353      */
checkOption(String option, String value)354     protected boolean checkOption(String option, String value) {
355 
356         // defines directly, server or proxytest mode
357         if (option.equals(TEST_MODE)) {
358             if ( (!value.equals(DIRECTLY_MODE)) &&
359                  (!value.equals(SERVER_MODE)) &&
360                  (!value.equals(PROXY_MODE))
361                ) {
362                 throw new BadOption(option + ": must be one of: "
363                                   + "\"" + DIRECTLY_MODE + "\", "
364                                   + "\"" + SERVER_MODE   + "\", "
365                                   + "\"" + PROXY_MODE    + "\"");
366             }
367             return true;
368         }
369 
370         // defines invocation type for stack filling
371         if (option.equals(INVOCATION_TYPE)) {
372             if ( (!value.equals(JAVA_TYPE)) &&
373                  (!value.equals(NATIVE_TYPE)) &&
374                  (!value.equals(MIXED_TYPE))
375                ) {
376                 throw new BadOption(option + ": must be one of: "
377                                   + "\"" + JAVA_TYPE + "\", "
378                                   + "\"" + NATIVE_TYPE + "\", "
379                                   + "\"" + MIXED_TYPE + "\"");
380             }
381             return true;
382         }
383 
384         // defines default or custom MBean server
385         if (option.equals(SERVER_TYPE)) {
386             if ((!value.equals(DEFAULT_TYPE))
387                 && (!value.equals(CUSTOM_TYPE))) {
388                 throw new BadOption(option + ": must be one of: \""
389                                            + DEFAULT_TYPE + "\", \""
390                                            + CUSTOM_TYPE + "\"");
391             }
392 
393             return true;
394         }
395 
396         // defines loadable classes and loaders counts
397         if (option.equals(LOADABLE_CLASSES_COUNT) ||
398                 option.equals(LOADERS_COUNT) ||
399                 option.equals(THREAD_DEPTH) || option.equals(THREAD_COUNT)) {
400             try {
401                 int number = Integer.parseInt(value);
402                 if (number < 0) {
403                     throw new BadOption(option + ": value must be a positive "
404                                       + "integer");
405                 }
406             } catch (NumberFormatException e) {
407                 throw new BadOption(option + ": value must be an integer");
408             }
409             return true;
410         }
411 
412         // defines timeout
413         if (option.equals(TIMEOUT)) {
414             try {
415                 int number = Integer.parseInt(value);
416 
417                 if (number < 0)
418                     throw new BadOption(option + ": value must be a positive "
419                                       + "integer");
420             } catch (NumberFormatException e) {
421                 throw new BadOption(option + ": value must be an integer");
422             }
423             return true;
424         }
425 
426         // defines if classloader class is single
427         if (option.equals(SINGLE_CLASSLOADER_CLASS)) {
428             if (!(value == null || value.length() <= 0)) {
429                 throw new BadOption(option + ": no value must be specified");
430             }
431             return true;
432         }
433 
434         // defines memory types
435         if (option.equals(MEMORY_TYPE)) {
436             if ( (!value.equals(MT_HEAP)) &&
437                  (!value.equals(MT_NONHEAP)) &&
438                  (!value.equals(MT_MIXED))
439                )
440                 throw new BadOption(option + ": must be one of: "
441                                   + "\"" + MT_HEAP + "\", "
442                                   + "\"" + MT_NONHEAP + "\", "
443                                   + "\"" + MT_MIXED + "\"");
444             return true;
445         }
446 
447         // defines type of monitoring
448         if (option.equals(MONITORING)) {
449             if ( (!value.equals(MON_POLLING)) &&
450                  (!value.equals(MON_NOTIF))
451                )
452                 throw new BadOption(option + ": must be one of: "
453                                   + "\"" + MON_POLLING + "\", "
454                                   + "\"" + MON_NOTIF + "\"");
455             return true;
456         }
457 
458         // defines threshold
459         if (option.equals(THRESHOLD)) {
460             if ( (!value.equals(TH_USAGE)) &&
461                  (!value.equals(TH_COLLECTION))
462                )
463                 throw new BadOption(option + ": must be one of: "
464                                   + "\"" + TH_USAGE + "\", "
465                                   + "\"" + TH_COLLECTION + "\"");
466             return true;
467         }
468 
469         if (option.equals(SCENARIO_TYPE)) {
470                 return true;
471         }
472         if (option.equals(ITERATIONS)) {
473             try {
474                 int number = Integer.parseInt(value);
475 
476                 if (number < 0)
477                     throw new BadOption(option + ": value must be a positive "
478                                       + "integer");
479                 return true;
480             } catch (NumberFormatException e) {
481                 throw new BadOption(option + ": value must be an integer");
482             }
483 
484         }
485         return super.checkOption(option, value);
486     }
487 
488     /**
489      * Check if the values of all options are consistent.
490      * This method is invoked by <code>parseArguments()</code>
491      *
492      * @throws <i>BadOption</i> if options have inconsistent values
493      *
494      * @see nsk.share.ArgumentParser#parseArguments
495      */
checkOptions()496     protected void checkOptions() {
497         super.checkOptions();
498     }
499 
dump(Log log)500     public void dump(Log log) {
501             log.info("Test mode: " + getTestMode());
502             log.info("Server type: " + getServerType());
503             log.info("loadableClassesCount: " + getLoadableClassesCount());
504             log.info("loadersCount: " + getLoadersCount());
505             log.info("singleClassloaderClass: " + singleClassloaderClass());
506     }
507 } // ArgumentHandler
508