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

org.wildfly.swarm.cli.CommandLine Maven / Gradle / Ivy

/**
 * Copyright 2015-2017 Red Hat, Inc, and individual contributors.
 *
 * 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.
 */
package org.wildfly.swarm.cli;

import java.io.IOException;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;

import javax.enterprise.inject.Vetoed;

import org.jboss.modules.Module;
import org.jboss.modules.ModuleClassLoader;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoadException;
import org.wildfly.swarm.Swarm;
import org.wildfly.swarm.SwarmInfo;
import org.wildfly.swarm.spi.api.SwarmProperties;

/**
 * A parsed command-line.
 *
 * @author Bob McWhirter
 */
@Vetoed
public class CommandLine {

    private static final String CONFIG_ELEMENT = "";

    private static final String FRACTION = "fraction";

    private static final String ALL = "all";

    /**
     * Default option for parsing -h and --help
     */
    public static final Option HELP = new Option()
            .withLong("help")
            .withShort('h')
            .withDescription("Display this help")
            .withDefault(() -> false)
            .then((cmd, opt, value) -> cmd.put(opt, true));

    public static final Option CONFIG_HELP = new Option()
            .withLong("config-help")
            .hasValue("")
            .withDescription("Display configuration help by fraction, or 'all' for all")
            .then((cmd, opt, value) -> cmd.put(opt, value));

    public static final Option YAML_HELP = new Option()
            .withLong("yaml-help")
            .hasValue("")
            .withDescription("Display example YAML configuration by fraction, or 'all' for all")
            .then((cmd, opt, value) -> cmd.put(opt, value));

    /**
     * Default option for parsing -v and --version
     */
    public static final Option VERSION = new Option()
            .withLong("version")
            .withShort('v')
            .withDescription("Display the version of WildFly Swarm")
            .withDefault(() -> false)
            .then((cmd, opt, value) -> cmd.put(opt, true));

    /**
     * Default option for parsing -Dname and -Dname=value
     */
    public static final Option PROPERTY = new Option()
            .withShort('D')
            .hasValue("[=]")
            .valueMayBeSeparate(false)
            .withDescription("Set a system property")
            .withDefault(Properties::new)
            .then((cmd, opt, value) -> {
                String[] nameValue = value.split("=");
                Properties props = cmd.get(opt);
                String propName = nameValue[0];
                String propValue = "true";
                if (nameValue.length > 1) {
                    propValue = nameValue[1];
                }
                props.setProperty(propName, propValue);
            });

    /**
     * Default option for parsing -P
     */
    public static final Option PROPERTIES_URL = new Option()
            .withShort('P')
            .withLong("properties")
            .hasValue("")
            .withDescription("Load system properties from the given URL")
            .then((cmd, opt, value) -> cmd.put(opt, Option.toURL(value)));

    /**
     * Default option for parsing -c and --server-config
     */
    public static final Option SERVER_CONFIG = new Option()
            .withShort('c')
            .withLong("server-config")
            .hasValue(CONFIG_ELEMENT)
            .valueMayBeSeparate(true)
            .withDescription("URL of the server configuration (e.g. standalone.xml)")
            .withDefault(() -> resolveResource("standalone.xml"))
            .then((cmd, opt, value) -> cmd.put(opt, Option.toURL(value)));


    public static final Option> CONFIG = new Option>()
            .withShort('s')
            .withLong("config")
            .hasValue("")
            .valueMayBeSeparate(true)
            .withDescription("URL to configuration YAML to use")
            .then((cmd, opt, value) -> {
                List configs = cmd.get(opt);
                if (configs == null) {
                    configs = new ArrayList<>();
                    cmd.put(opt, configs);
                }
                configs.add(Option.toURL(value));
            });

    public static final Option> PROFILES = new Option>()
            .withShort('S')
            .withLong("profile")
            .hasValue("")
            .valueMayBeSeparate(true)
            .withDescription("Selected profiles")
            .then((cmd, opt, value) -> {
                List profiles = cmd.get(opt);
                if (profiles == null) {
                    profiles = new ArrayList<>();
                    cmd.put(opt, profiles);
                }
                profiles.add(value);
            });

    /**
     * Default option for parsing -b
     */
    public static final Option BIND = new Option()
            .withShort('b')
            .hasValue("")
            .valueMayBeSeparate(true)
            .withDescription("Set the property " + SwarmProperties.BIND_ADDRESS + " to ")
            .then(CommandLine::put);

    /**
     * Default set of options
     */
    public static Options defaultOptions() {
        return new Options(
                HELP,
                CONFIG_HELP,
                YAML_HELP,
                VERSION,
                PROPERTY,
                PROPERTIES_URL,
                SERVER_CONFIG,
                CONFIG,
                PROFILES,
                BIND
        );
    }

    CommandLine(Options options) {
        this.options = options;
    }

    /**
     * Put a value under a given key.
     *
     * @param key   The key.
     * @param value The value.
     * @param    The type of the value.
     */
    public  void put(Option key, T value) {
        this.values.put(key, value);
    }

    /**
     * Retrieve a value under a given key.
     *
     * @param key The key.
     * @param  The type of the value.
     * @return The previously stored value, or the default provided by key if none has been previously stored.  The default will then be stored.
     */
    @SuppressWarnings("unchecked")
    public  T get(Option key) {
        T v = (T) this.values.get(key);
        if (v == null) {
            v = key.defaultValue();
            this.values.put(key, v);
        }
        return v;
    }

    /**
     * Display help for the options associated with the creation of this command-line.
     *
     * @param out The output stream to display help upon.
     */
    public void displayHelp(PrintStream out) {
        this.options.displayHelp(out);
    }

    public void displayConfigHelp(PrintStream out, String fraction) throws IOException, ModuleLoadException {
        ModuleClassLoader cl = Module.getBootModuleLoader().loadModule(ModuleIdentifier.create("swarm.application")).getClassLoader();
        Enumeration docs = cl.getResources("META-INF/configuration-meta.properties");

        Properties props = new Properties();

        while (docs.hasMoreElements()) {
            URL each = docs.nextElement();
            Properties fractionDocs = new Properties();
            fractionDocs.load(each.openStream());
            if (fraction.equals(ALL) || fraction.equals(fractionDocs.getProperty(FRACTION))) {
                fractionDocs.remove(FRACTION);
                props.putAll(fractionDocs);
            }
        }

        props.stringPropertyNames().stream()
                .sorted()
                .forEach(key -> {
                    out.println("# " + key);
                    out.println();
                    out.println(formatDocs("    ", props.getProperty(key)));
                    out.println();
                });
    }

    public void dumpYaml(PrintStream out, String fraction) throws IOException, ModuleLoadException {
        ModuleClassLoader cl = Module.getBootModuleLoader().loadModule(ModuleIdentifier.create("swarm.application")).getClassLoader();
        Enumeration docs = cl.getResources("META-INF/configuration-meta.properties");

        Properties props = new Properties();

        while (docs.hasMoreElements()) {
            URL each = docs.nextElement();
            Properties fractionDocs = new Properties();
            fractionDocs.load(each.openStream());
            if (fraction.equals(ALL) || fraction.equals(fractionDocs.getProperty(FRACTION))) {
                fractionDocs.remove(FRACTION);
                props.putAll(fractionDocs);
            }
        }

        YamlDumper.dump(out, props);
    }

    private String formatDocs(String indent, String docs) {

        StringTokenizer tokens = new StringTokenizer(docs);
        StringBuilder formatted = new StringBuilder();

        int lineLength = indent.length();
        boolean freshLine = true;

        formatted.append(indent);

        while (tokens.hasMoreElements()) {
            String next = tokens.nextToken();

            if ((lineLength + 1 + next.length()) > 80) {
                formatted.append("\n");
                formatted.append(indent);
                lineLength = indent.length();
                freshLine = true;
            }

            if (freshLine) {
                freshLine = false;
            } else {
                formatted.append(" ");
            }

            lineLength += next.length();
            formatted.append(next);
        }

        return formatted.toString();
    }

    /**
     * Display the version.
     *
     * @param out The output stream to display help upon.
     */
    public void displayVersion(PrintStream out) {
        out.println("WildFly Swarm version " + SwarmInfo.VERSION);
    }

    /**
     * Apply properties to the system properties.
     *
     * 

Applies values stored through the Key.PROPERTIES, * Key.PROPERTIES_URL or Key.BIND options. * * @throws IOException If a URL is attempted to be read and fails. */ public void applyProperties(Swarm swarm) throws IOException { URL propsUrl = get(PROPERTIES_URL); if (propsUrl != null) { Properties urlProps = new Properties(); urlProps.load(propsUrl.openStream()); for (String name : urlProps.stringPropertyNames()) { swarm.withProperty(name, urlProps.getProperty(name)); } } Properties props = get(PROPERTY); for (String name : props.stringPropertyNames()) { swarm.withProperty(name, props.getProperty(name)); } if (get(BIND) != null) { swarm.withProperty(SwarmProperties.BIND_ADDRESS, get(BIND)); } } /** * Apply configuration to the container. * *

Applies configuration from Key.SERVER_CONFIG and Key.STAGE_CONFIG.

* * @param swarm Swarm instance to configure. * @throws MalformedURLException If a URL is attempted to be read and fails. */ public void applyConfigurations(Swarm swarm) throws IOException { if (get(SERVER_CONFIG) != null) { swarm.withXmlConfig(get(SERVER_CONFIG)); } if (get(CONFIG) != null) { List configs = get(CONFIG); for (URL config : configs) { swarm.withConfig(config); } } if (get(PROFILES) != null) { List profiles = get(PROFILES); for (String profile : profiles) { swarm.withProfile(profile); } } } /** * Apply properties and configuration from the parsed commandline to a container. * * @param swarm The Swarm instance to apply configuration to. * @throws IOException If an error occurs resolving any URL. */ public void apply(Swarm swarm) throws IOException, ModuleLoadException { applyProperties(swarm); applyConfigurations(swarm); if (get(HELP)) { displayVersion(System.err); System.err.println(); displayHelp(System.err); System.exit(0); } if (get(CONFIG_HELP) != null) { displayConfigHelp(System.err, get(CONFIG_HELP)); System.exit(0); } if (get(YAML_HELP) != null) { dumpYaml(System.err, get(YAML_HELP)); System.exit(0); } if (get(VERSION)) { displayVersion(System.err); } } void extraArgument(String arg) { this.extraArguments.add(arg); } /** * Any un-parsed non-option arguments. * * @return The list of unparsed arguments. */ public List extraArguments() { return this.extraArguments; } /** * Any un-parsed non-option arguments. * * @return The array of unparsed arguments. */ public String[] extraArgumentsArray() { return this.extraArguments.toArray(new String[this.extraArguments.size()]); } void invalidArgument(String arg) { this.invalidArguments.add(arg); } /** * Any invalid options seen during parsing. * * @return The list of invalid arguments. */ public List invalidArguments() { return this.invalidArguments; } /** * Determine if any invalid arguments were seen during the parse. * * @return true if {@link #invalidArguments()} is not empty, otherwise false. */ public boolean hasInvalidArguments() { return !this.invalidArguments.isEmpty(); } /** * Parse an array of arguments using the default options. * * @param args The args to parse. * @return The parsed CommandLine. */ public static CommandLine parse(String... args) throws Exception { return CommandLineParser.parse(defaultOptions(), args); } /** * Parse an array of arguments using specific options. * * @param options The options to use. * @param args The args to parse. * @return The parsed CommandLine. */ public static CommandLine parse(Options options, String... args) throws Exception { return CommandLineParser.parse(options, args); } private static URL resolveResource(String path) { Path candidate = Paths.get(path); if (Files.exists(candidate)) { try { return candidate.toUri().toURL(); } catch (MalformedURLException e) { // ignore } } URL yml = null; try { Module appModule = Module.getBootModuleLoader().loadModule(ModuleIdentifier.create("swarm.application")); yml = appModule.getClassLoader().getResource(path); if (yml != null) { return yml; } } catch (ModuleLoadException e) { // ignore; } yml = ClassLoader.getSystemClassLoader().getResource(path); return yml; } private final Options options; private final Map, Object> values = new HashMap<>(); private final List extraArguments = new ArrayList<>(); private final List invalidArguments = new ArrayList<>(); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy