001: /*
002: * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.tools.jstat;
027:
028: import java.io.*;
029: import java.net.*;
030: import java.util.*;
031: import java.util.regex.*;
032: import sun.jvmstat.monitor.Monitor;
033: import sun.jvmstat.monitor.VmIdentifier;
034:
035: /**
036: * Class for processing command line arguments and providing method
037: * level access to arguments.
038: *
039: * @author Brian Doherty
040: * @version 1.11, 05/09/07
041: * @since 1.5
042: */
043: public class Arguments {
044:
045: private static final boolean debug = Boolean
046: .getBoolean("jstat.debug");
047: private static final boolean showUnsupported = Boolean
048: .getBoolean("jstat.showUnsupported");
049:
050: private static final String JVMSTAT_USERDIR = ".jvmstat";
051: private static final String OPTIONS_FILENAME = "jstat_options";
052: private static final String ALL_NAMES = "\\w*";
053:
054: private Comparator<Monitor> comparator;
055: private int headerRate;
056: private boolean help;
057: private boolean list;
058: private boolean options;
059: private boolean constants;
060: private boolean constantsOnly;
061: private boolean strings;
062: private boolean timestamp;
063: private boolean snap;
064: private boolean verbose;
065: private String specialOption;
066: private String names;
067:
068: private OptionFormat optionFormat;
069:
070: private int count = -1;
071: private int interval = -1;
072: private String vmIdString;
073:
074: private VmIdentifier vmId;
075:
076: public static void printUsage(PrintStream ps) {
077: ps.println("Usage: jstat -help|-options");
078: ps
079: .println(" jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]");
080: ps.println();
081: ps.println("Definitions:");
082: ps
083: .println(" <option> An option reported by the -options option");
084: ps
085: .println(" <vmid> Virtual Machine Identifier. A vmid takes the following form:");
086: ps
087: .println(" <lvmid>[@<hostname>[:<port>]]");
088: ps
089: .println(" Where <lvmid> is the local vm identifier for the target");
090: ps
091: .println(" Java virtual machine, typically a process id; <hostname> is");
092: ps
093: .println(" the name of the host running the target Java virtual machine;");
094: ps
095: .println(" and <port> is the port number for the rmiregistry on the");
096: ps
097: .println(" target host. See the jvmstat documentation for a more complete");
098: ps
099: .println(" description of the Virtual Machine Identifier.");
100: ps
101: .println(" <lines> Number of samples between header lines.");
102: ps
103: .println(" <interval> Sampling interval. The following forms are allowed:");
104: ps.println(" <n>[\"ms\"|\"s\"]");
105: ps
106: .println(" Where <n> is an integer and the suffix specifies the units as ");
107: ps
108: .println(" milliseconds(\"ms\") or seconds(\"s\"). The default units are \"ms\".");
109: ps
110: .println(" <count> Number of samples to take before terminating.");
111: ps
112: .println(" -J<flag> Pass <flag> directly to the runtime system.");
113:
114: // undocumented options:
115: // -list [<vmid>] - list counter names
116: // -snap <vmid> - snapshot counter values as name=value pairs
117: // -name <pattern> - output counters matching given pattern
118: // -a - sort in ascending order (default)
119: // -d - sort in descending order
120: // -v - verbose output (-snap)
121: // -constants - output constants with -name output
122: // -strings - output strings with -name output
123: }
124:
125: private static int toMillis(String s)
126: throws IllegalArgumentException {
127:
128: String[] unitStrings = { "ms", "s" }; // ordered from most specific to
129: // least specific
130: String unitString = null;
131: String valueString = s;
132:
133: for (int i = 0; i < unitStrings.length; i++) {
134: int index = s.indexOf(unitStrings[i]);
135: if (index > 0) {
136: unitString = s.substring(index);
137: valueString = s.substring(0, index);
138: break;
139: }
140: }
141:
142: try {
143: int value = Integer.parseInt(valueString);
144:
145: if (unitString == null || unitString.compareTo("ms") == 0) {
146: return value;
147: } else if (unitString.compareTo("s") == 0) {
148: return value * 1000;
149: } else {
150: throw new IllegalArgumentException("Unknow time unit: "
151: + unitString);
152: }
153: } catch (NumberFormatException e) {
154: throw new IllegalArgumentException(
155: "Could not convert interval: " + s);
156: }
157: }
158:
159: public Arguments(String[] args) throws IllegalArgumentException {
160: int argc = 0;
161:
162: if (args.length < 1) {
163: throw new IllegalArgumentException("invalid argument count");
164: }
165:
166: if ((args[0].compareTo("-?") == 0)
167: || (args[0].compareTo("-help") == 0)) {
168: help = true;
169: return;
170: } else if (args[0].compareTo("-options") == 0) {
171: options = true;
172: return;
173: } else if (args[0].compareTo("-list") == 0) {
174: list = true;
175: if (args.length > 2) {
176: throw new IllegalArgumentException(
177: "invalid argument count");
178: }
179: // list can take one arg - a vmid - fall through for arg processing
180: argc++;
181: }
182:
183: for (; (argc < args.length) && (args[argc].startsWith("-")); argc++) {
184: String arg = args[argc];
185:
186: if (arg.compareTo("-a") == 0) {
187: comparator = new AscendingMonitorComparator();
188: } else if (arg.compareTo("-d") == 0) {
189: comparator = new DescendingMonitorComparator();
190: } else if (arg.compareTo("-t") == 0) {
191: timestamp = true;
192: } else if (arg.compareTo("-v") == 0) {
193: verbose = true;
194: } else if ((arg.compareTo("-constants") == 0)
195: || (arg.compareTo("-c") == 0)) {
196: constants = true;
197: } else if ((arg.compareTo("-strings") == 0)
198: || (arg.compareTo("-s") == 0)) {
199: strings = true;
200: } else if (arg.startsWith("-h")) {
201: String value;
202: if (arg.compareTo("-h") != 0) {
203: value = arg.substring(2);
204: } else {
205: argc++;
206: if (argc >= args.length) {
207: throw new IllegalArgumentException(
208: "-h requires an integer argument");
209: }
210: value = args[argc];
211: }
212: try {
213: headerRate = Integer.parseInt(value);
214: } catch (NumberFormatException e) {
215: headerRate = -1;
216: }
217: if (headerRate < 0) {
218: throw new IllegalArgumentException(
219: "illegal -h argument: " + value);
220: }
221: } else if (arg.startsWith("-name")) {
222: if (arg.startsWith("-name=")) {
223: names = arg.substring(7);
224: } else {
225: argc++;
226: if (argc >= args.length) {
227: throw new IllegalArgumentException(
228: "option argument expected");
229: }
230: names = args[argc];
231: }
232: } else {
233: /*
234: * there are scenarios here: special jstat_options file option
235: * or the rare case of a negative lvmid. The negative lvmid
236: * can occur in some operating environments (such as Windows
237: * 95/98/ME), so we provide for this case here by checking if
238: * the argument has any numerical characters. This assumes that
239: * there are no special jstat_options that contain numerical
240: * characters in their name.
241: */
242:
243: // extract the lvmid part of possible lvmid@host.domain:port
244: String lvmidStr = null;
245: int at_index = args[argc].indexOf('@');
246: if (at_index < 0) {
247: lvmidStr = args[argc];
248: } else {
249: lvmidStr = args[argc].substring(0, at_index);
250: }
251:
252: // try to parse the lvmid part as an integer
253: try {
254: int vmid = Integer.parseInt(lvmidStr);
255: // it parsed, assume a negative lvmid and continue
256: break;
257: } catch (NumberFormatException nfe) {
258: // it didn't parse. check for the -snap or jstat_options
259: // file options.
260: if ((argc == 0)
261: && (args[argc].compareTo("-snap") == 0)) {
262: snap = true;
263: } else if (argc == 0) {
264: specialOption = args[argc].substring(1);
265: } else {
266: throw new IllegalArgumentException(
267: "illegal argument: " + args[argc]);
268: }
269: }
270: }
271: }
272:
273: // prevent 'jstat <pid>' from being accepted as a valid argument
274: if (!(specialOption != null || list || snap || names != null)) {
275: throw new IllegalArgumentException("-<option> required");
276: }
277:
278: switch (args.length - argc) {
279: case 3:
280: if (snap) {
281: throw new IllegalArgumentException(
282: "invalid argument count");
283: }
284: try {
285: count = Integer.parseInt(args[args.length - 1]);
286: } catch (NumberFormatException e) {
287: throw new IllegalArgumentException(
288: "illegal count value: " + args[args.length - 1]);
289: }
290: interval = toMillis(args[args.length - 2]);
291: vmIdString = args[args.length - 3];
292: break;
293: case 2:
294: if (snap) {
295: throw new IllegalArgumentException(
296: "invalid argument count");
297: }
298: interval = toMillis(args[args.length - 1]);
299: vmIdString = args[args.length - 2];
300: break;
301: case 1:
302: vmIdString = args[args.length - 1];
303: break;
304: case 0:
305: if (!list) {
306: throw new IllegalArgumentException(
307: "invalid argument count");
308: }
309: break;
310: default:
311: throw new IllegalArgumentException("invalid argument count");
312: }
313:
314: // set count and interval to their default values if not set above.
315: if (count == -1 && interval == -1) {
316: // default is for a single sample
317: count = 1;
318: interval = 0;
319: }
320:
321: // validate arguments
322: if (comparator == null) {
323: comparator = new AscendingMonitorComparator();
324: }
325:
326: // allow ',' characters to separate names, convert to '|' chars
327: names = (names == null) ? ALL_NAMES : names.replace(',', '|');
328:
329: // verify that the given pattern parses without errors
330: try {
331: Pattern pattern = Pattern.compile(names);
332: } catch (PatternSyntaxException e) {
333: throw new IllegalArgumentException("Bad name pattern: "
334: + e.getMessage());
335: }
336:
337: // verify that the special option is valid and get it's formatter
338: if (specialOption != null) {
339: OptionFinder finder = new OptionFinder(optionsSources());
340: optionFormat = finder.getOptionFormat(specialOption,
341: timestamp);
342: if (optionFormat == null) {
343: throw new IllegalArgumentException("Unknown option: -"
344: + specialOption);
345: }
346: }
347:
348: // verify that the vm identifier is valied
349: try {
350: vmId = new VmIdentifier(vmIdString);
351: } catch (URISyntaxException e) {
352: IllegalArgumentException iae = new IllegalArgumentException(
353: "Malformed VM Identifier: " + vmIdString);
354: iae.initCause(e);
355: throw iae;
356: }
357: }
358:
359: public Comparator<Monitor> comparator() {
360: return comparator;
361: }
362:
363: public boolean isHelp() {
364: return help;
365: }
366:
367: public boolean isList() {
368: return list;
369: }
370:
371: public boolean isSnap() {
372: return snap;
373: }
374:
375: public boolean isOptions() {
376: return options;
377: }
378:
379: public boolean isVerbose() {
380: return verbose;
381: }
382:
383: public boolean printConstants() {
384: return constants;
385: }
386:
387: public boolean isConstantsOnly() {
388: return constantsOnly;
389: }
390:
391: public boolean printStrings() {
392: return strings;
393: }
394:
395: public boolean showUnsupported() {
396: return showUnsupported;
397: }
398:
399: public int headerRate() {
400: return headerRate;
401: }
402:
403: public String counterNames() {
404: return names;
405: }
406:
407: public VmIdentifier vmId() {
408: return vmId;
409: }
410:
411: public String vmIdString() {
412: return vmIdString;
413: }
414:
415: public int sampleInterval() {
416: return interval;
417: }
418:
419: public int sampleCount() {
420: return count;
421: }
422:
423: public boolean isTimestamp() {
424: return timestamp;
425: }
426:
427: public boolean isSpecialOption() {
428: return specialOption != null;
429: }
430:
431: public String specialOption() {
432: return specialOption;
433: }
434:
435: public OptionFormat optionFormat() {
436: return optionFormat;
437: }
438:
439: public URL[] optionsSources() {
440: URL[] sources = new URL[2];
441: int i = 0;
442:
443: String filename = OPTIONS_FILENAME;
444:
445: try {
446: String userHome = System.getProperty("user.home");
447: String userDir = userHome + "/" + JVMSTAT_USERDIR;
448: File home = new File(userDir + "/" + filename);
449: sources[i++] = home.toURL();
450: } catch (Exception e) {
451: if (debug) {
452: System.err.println(e.getMessage());
453: e.printStackTrace();
454: }
455: throw new IllegalArgumentException(
456: "Internal Error: Bad URL: " + e.getMessage());
457: }
458: sources[i] = this .getClass().getResource(
459: "resources/" + filename);
460: assert sources[i] != null;
461: return sources;
462: }
463: }
|