All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jolokia.jvmagent.client.util.OptionsAndArgs Maven / Gradle / Ivy

The newest version!
package org.jolokia.jvmagent.client.util;

/*
 * Copyright 2009-2013 Roland Huss
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.File;
import java.net.URISyntaxException;
import java.util.*;
import java.util.regex.*;

import org.jolokia.server.core.util.EscapeUtil;

/**
 * Class representing options and arguments known to the client launcher. It also knows how
 * to parse the command line.
 *
 * @author roland
 * @since 12.08.11
 */
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public final class OptionsAndArgs {

    // ===================================================================================
    // Available options

    private static final Map SHORT_OPTS = new HashMap<>();
    private static final Set OPTIONS = new HashSet<>(Arrays.asList(
            // JVM Agent options:
            "host", "port", "agentContext", "user", "password",
            "quiet!", "verbose!", "version!", "executor", "threadNamePrefix", "threadNr",
            "backlog", "hide!", "protocol", "authMode", "authClass",
            "authUrl", "authPrincipalSpec", "authIgnoreCerts!",
            //https options:
            "keystore", "keystorePassword", "useSslClientAuthentication!",
            "secureSocketProtocol", "keyStoreType", "keyManagerAlgorithm", "trustManagerAlgorithm",
            "caCert", "serverCert", "serverKey", "serverKeyAlgorithm", "clientPrincipal", "extractClientCheck",
            "sslProtocol", "sslCipherSuite",
            // Jolokia options:
            "historyMaxEntries", "debug!", "debugMaxEntries",
            "logHandlerClass", "logHandlerName", "maxDepth", "maxCollectionSize",
            "maxObjects", "serializeLong", "restrictorClass", "policyLocation", "mbeanQualifier",
            "disableDetectors!", "enabledServices", "disabledServices",
            "canonicalNaming", "includeStackTrace", "serializeException", "includeRequest",
            "dateFormat", "dateFormatTimeZone",
            "discoveryEnabled", "discoveryAgentUrl", "agentId", "agentDescription",
            // Others:
            "config", "help!"));

    private static final Set LIST_OPTIONS = new HashSet<>(Arrays.asList(
            "clientPrincipal", "sslProtocol", "sslCipherSuite"));

    static {
        String[] shortOptsDef = {
            "h", "help",
            "u", "user",
            "p", "password",
            "c", "agentContext",
            "v", "verbose",
            "q", "quiet"
        };

        for (int i = 0; i < shortOptsDef.length; i += 2) {
            SHORT_OPTS.put(shortOptsDef[i],shortOptsDef[i+1]);
        }
    }

    // Command which require a PID as argument
    private static final Set COMMANDS_REQUIRING_PID =
            new HashSet<>(Arrays.asList("start", "stop", "toggle", "status"));

    // Launcher command
    private String command;

    // Extra arguments
    private List extraArgs;

    private final Map options;

    private boolean quiet;
    private boolean verbose;

    // Jar file where this class is in
    private File jarFile;

    /**
     * Parse a list of arguments. Options start with '--' (long form) or '-' (short form) and are
     * defined in {@see OPTIONS} and {@see SHORT_OPTS}. For options with arguments, the argument can
     * bei either provided in the form '--option=value' or '--option value'. Everything which is
     * not an option is considered to be an argument. Two arguments are allowed: The command
     * (first) and the PID (second). Any non-numeric PID is considered to be a pattern. Either {@link #getPid()} or
     * {@link #getProcessPattern()} is set.
     * 

* If no PID/pattern and no command is given the "list" command is implied. If as first argument a pure numeric value * or a pattern (which must not be equal to a valid command) is given, then "toggle" is infered with the given * PID/pattern. * * @param pCommands set of commands which are known * @param pArgs arguments as given on the command line * @throws IllegalArgumentException if parsing fails */ public OptionsAndArgs(Set pCommands, String ... pArgs) { options = new HashMap<>(); // Parse options List arguments = new ArrayList<>(); for (int i = 0; i < pArgs.length; i++) { String arg = pArgs[i]; if (arg.startsWith("-")) { ArgParsed argParsed = parseArgument(pArgs[i], i + 1 <= pArgs.length - 1 ? pArgs[i + 1] : null); if (argParsed.skipNext) { i++; } String optionsKey = argParsed.option + (LIST_OPTIONS.contains(argParsed.option) ? getNextListIndexSuffix(options, argParsed.option) : ""); options.put(optionsKey, argParsed.value); } else { arguments.add(arg); } } command = !arguments.isEmpty() ? arguments.remove(0) : null; String[] args = !arguments.isEmpty() ? arguments.toArray(new String[0]) : new String[0]; init(pCommands, args); } /** * Convert options to agent readable options (i.e. a single string with options separated by commas) * * @return agent string */ public String toAgentArg() { StringBuilder arg = new StringBuilder(); for (Map.Entry entry : options.entrySet()) { String key = entry.getKey(); if (!key.equals("quiet") && !key.equals("verbose")) { arg.append(key).append("=").append(EscapeUtil.escape(entry.getValue(), EscapeUtil.CSV_ESCAPE, ",")).append(","); } } return arg.length() > 0 ? arg.substring(0,arg.length() - 1) : ""; } /** * Process id as given as argument (if any). If a pattern for matching the process name is used, this * method returns null. * * @return process id or null */ public String getPid() { String arg = !extraArgs.isEmpty() ? extraArgs.get(0) : null; return arg != null && arg.matches("^\\d+$") ? arg : null; } /** * A pattern used for matching a process name. If {@link #getPid()} return a non-null value, * this method returns always null * * @return pattern to match a process name or null */ public Pattern getProcessPattern() { String arg = !extraArgs.isEmpty() ? extraArgs.get(0) : null; try { return arg != null && getPid() == null ? Pattern.compile(arg, Pattern.CASE_INSENSITIVE) : null; } catch (PatternSyntaxException exp) { throw new IllegalArgumentException("Invalid pattern '" + arg + "' for matching process names", exp); } } /** * The command given as argument * * @return command */ public String getCommand() { return command; } /** * Get extra arguments in addition to the command, or an empty list */ public List getExtraArgs() { return extraArgs; } /** * Whether the program should be silent * @return true if quiet mode is selected */ public boolean isQuiet() { return quiet; } /** * Get the configured port */ public String getPort() { String port = options.get("port"); return port != null ? port : "8778"; } /** * Verbose output if this is true * * @return true if verbose output is requested */ public boolean isVerbose() { return verbose; } /** * Return true if this command required an attached VM or false otherwise * * @return true if the command requires an attached VM */ public boolean needsVm() { return COMMANDS_REQUIRING_PID.contains(command) || "list".equals(command); } /** * Path to this agents jar file * * @return full path to jar file */ public String getJarFilePath() { return jarFile.getAbsolutePath(); } /** * Name of the agents jar file * * @return short name of jar file containing this agent. */ public String getJarFileName() { return jarFile.getName(); } /** * Lookup the JAR File from where this class is loaded * * @return File pointing to the JAR-File from where this class was loaded. */ public static File lookupJarFile() { try { return new File(OptionsAndArgs.class .getProtectionDomain() .getCodeSource() .getLocation() .toURI()); } catch (URISyntaxException e) { throw new IllegalStateException("Error: Cannot lookup jar for this class: " + e,e); } } // =============================================================== // Command line handling private static final Pattern ARGUMENT_PATTERN_WITH_EQUAL = Pattern.compile("([^=]+)=(.*)"); private ArgParsed parseArgument(String pArg, String pNextArgument) { return pArg.startsWith("--") ? parseLongOption(pArg, pNextArgument) : parseShortOption(pArg, pNextArgument); } private ArgParsed parseShortOption(String pArg, String pNextArgument) { // Short option String opt = pArg.substring(1); String longOpt = SHORT_OPTS.get(opt); if (longOpt == null) { throw new IllegalArgumentException("No short option '" + opt + "' known"); } return parseArgument("--" + longOpt,pNextArgument); } private ArgParsed parseLongOption(String pArg, String pNextArgument) { // Long option String opt = pArg.substring(2); String value = null; // Check for format 'key=value' as argument Matcher matcher = ARGUMENT_PATTERN_WITH_EQUAL.matcher(opt); if (matcher.matches()) { opt = matcher.group(1); value = matcher.group(2); } if (OPTIONS.contains(opt)) { verifyOptionWithArgument(opt, value, pNextArgument); return value != null ? new ArgParsed(opt, value, false) : new ArgParsed(opt,pNextArgument,true); } else if (OPTIONS.contains(opt + "!")) { return new ArgParsed(opt,"true",false); } else { throw new IllegalArgumentException("Unknown option '" + opt + "'"); } } // check for the next key with a suffix like ".1" which is not already set private String getNextListIndexSuffix(Map options, String key) { if (!options.containsKey(key)) { return ""; } else { int i = 1; while (options.containsKey(key + "." + i)) { i++; } return "." + i; } } private void verifyOptionWithArgument(String pOpt, String pValue, String pNextArgument) { // Option with argument if (pValue == null && (pNextArgument == null || pNextArgument.startsWith("-"))) { throw new IllegalArgumentException("Option '" + pOpt + "' requires an argument"); } } // Initialise default command and validate private void init(Set pCommands, String ... pArgs) { quiet = options.containsKey("quiet"); verbose = options.containsKey("verbose"); jarFile = lookupJarFile(); // Special cases first extraArgs = checkCommandAndArgs(pCommands, pArgs); } private void verifyCommandAndArgs(String pCommand, List pArgs) { if (COMMANDS_REQUIRING_PID.contains(pCommand) && pArgs.isEmpty()) { throw new IllegalArgumentException("No process id (PID) or pattern given"); } } private List checkCommandAndArgs(Set pCommands, String ... pArgs) { List ret = new ArrayList<>(Arrays.asList(pArgs)); if (options.containsKey("help")) { command = "help"; } else if (options.containsKey("version")) { command = "version"; } else if (command != null && pArgs.length == 0 && !pCommands.contains(command)) { ret.add(command); command = "toggle"; } else if (command == null && pArgs.length == 0) { command = "list"; } verifyCommandAndArgs(command,ret); return ret; } // A parsed argument private static final class ArgParsed { private final boolean skipNext; private final String option; private final String value; private ArgParsed(String pOption, String pValue, boolean pSkipNext) { skipNext = pSkipNext; option = pOption; value = pValue; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy