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

org.apache.brooklyn.cli.AbstractMain Maven / Gradle / Ivy

Go to download

This project contains a org.apache.brooklyn.cli.Main class for command-line invocation.

There is a newer version: 1.1.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.brooklyn.cli;

import io.airlift.airline.Arguments;
import io.airlift.airline.Cli;
import io.airlift.airline.Cli.CliBuilder;
import io.airlift.airline.Command;
import io.airlift.airline.Help;
import io.airlift.airline.Option;
import io.airlift.airline.OptionType;
import io.airlift.airline.ParseException;

import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

import javax.inject.Inject;

import org.apache.brooklyn.api.catalog.BrooklynCatalog;
import org.apache.brooklyn.cli.Main.LaunchCommand;
import org.apache.brooklyn.core.BrooklynVersion;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.exceptions.FatalConfigurationRuntimeException;
import org.apache.brooklyn.util.exceptions.FatalRuntimeException;
import org.apache.brooklyn.util.exceptions.UserFacingException;
import org.apache.brooklyn.util.text.KeyValueParser;
import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;

/**
 * This class is the primary CLI for brooklyn.
 * Run with the `help` argument for help.
 * 

* This class is designed for subclassing, with subclasses typically: *

  • providing their own static {@link #main(String...)} (of course) which need simply invoke * {@link #execCli(String[])} with the arguments *
  • returning their CLI name (e.g. "start.sh") in an overridden {@link #cliScriptName()} *
  • providing an overridden {@link LaunchCommand} via {@link #cliLaunchCommand()} if desired *
  • providing any other CLI customisations by overriding {@link #cliBuilder()} * (typically calling the parent and then customizing the builder) *
  • populating a custom catalog using {@link LaunchCommand#populateCatalog(BrooklynCatalog)} */ public abstract class AbstractMain { private static final Logger log = LoggerFactory.getLogger(AbstractMain.class); // Launch banner public static final String DEFAULT_BANNER = " _ _ _ \n" + "| |__ _ __ ___ ___ | | _| |_ _ _ __ (R)\n" + "| '_ \\| '__/ _ \\ / _ \\| |/ / | | | | '_ \\ \n" + "| |_) | | | (_) | (_) | <| | |_| | | | |\n" + "|_.__/|_| \\___/ \\___/|_|\\_\\_|\\__, |_| |_|\n" + " |___/ "+BrooklynVersion.get()+"\n"; // Error codes public static final int SUCCESS = 0; public static final int PARSE_ERROR = 1; public static final int EXECUTION_ERROR = 2; public static final int CONFIGURATION_ERROR = 3; /** * Field intended for sub-classes (with their own {@code main()}) to customize the banner. * All accesses to the banner are done through this field, to ensure consistent customization. * * Note that a {@code getBanner()} method is not an option for supporting this, because * it is accessed from static inner-classes (such as {@link InfoCommand}, so non-static * methods are not an option (and one can't override static methods). */ protected static volatile String banner = DEFAULT_BANNER; /** abstract superclass for commands defining global options, but not arguments, * as that prevents Help from being injectable in the {@link HelpCommand} subclass */ public static abstract class BrooklynCommand implements Callable { @Option(type = OptionType.GLOBAL, name = { "-v", "--verbose" }, description = "Verbose mode") public boolean verbose; @Option(type = OptionType.GLOBAL, name = { "-q", "--quiet" }, description = "Quiet mode") public boolean quiet; @VisibleForTesting protected PrintStream stdout = System.out; @VisibleForTesting protected PrintStream stderr = System.err; @VisibleForTesting protected InputStream stdin = System.in; public ToStringHelper string() { return Objects.toStringHelper(getClass()) .add("verbose", verbose) .add("quiet", quiet); } @Override public String toString() { return string().toString(); } } /** common superclass for commands, defining global options (in our super) and extracting the arguments */ public static abstract class BrooklynCommandCollectingArgs extends BrooklynCommand { /** extra arguments */ @Arguments public List arguments = new ArrayList(); /** @return true iff there are arguments; it also sys.errs a warning in that case */ protected boolean warnIfArguments() { if (arguments.isEmpty()) return false; stderr.println("Invalid subcommand arguments: "+Strings.join(arguments, " ")); return true; } /** throw {@link ParseException} iff there are arguments */ protected void failIfArguments() { if (arguments.isEmpty()) return ; throw new ParseException("Invalid subcommand arguments '"+Strings.join(arguments, " ")+"'"); } @Override public ToStringHelper string() { return super.string() .add("arguments", arguments); } } /** superclass which reads `-D` system property definitions and applies them *

    * useful when scripting, e.g. where brooklyn.sh encodes `java o.a.b.Main "$@"` * but we want the caller to be able to pass system properties */ public static abstract class BrooklynCommandWithSystemDefines extends BrooklynCommandCollectingArgs { @Option(type = OptionType.GLOBAL, name = { "-D" }, description = "Set java system property") public List defines1; @Option(name = { "-D" }, description = "Set java system property") public List defines2; public List getDefines() { return MutableList.copyOf(defines1).appendAll(defines2); } public Map getDefinesAsMap() { return KeyValueParser.parseMap(Strings.join(getDefines(),",")); } public void applyDefinesAsSystemProperties() { System.getProperties().putAll(getDefinesAsMap()); } @Override public ToStringHelper string() { return super.string() .add("defines", getDefines()); } @Override public Void call() throws Exception { applyDefinesAsSystemProperties(); return null; } } @Command(name = "help", description = "Display help for available commands") public static class HelpCommand extends BrooklynCommand { @Inject public Help help; @Override public Void call() throws Exception { if (log.isDebugEnabled()) log.debug("Invoked help command: {}", this); return help.call(); } } @Command(name = "info", description = "Display information about brooklyn") public static class InfoCommand extends BrooklynCommandCollectingArgs { @Override public Void call() throws Exception { if (log.isDebugEnabled()) log.debug("Invoked info command: {}", this); warnIfArguments(); System.out.println(banner); System.out.println("Version: " + BrooklynVersion.get()); if (BrooklynVersion.INSTANCE.isSnapshot()) { System.out.println("Git SHA1: " + BrooklynVersion.INSTANCE.getSha1FromOsgiManifest()); } System.out.println("Website: http://brooklyn.apache.org"); System.out.println("Source: https://github.com/apache/brooklyn"); System.out.println(); System.out.println("Copyright 2011-2016 The Apache Software Foundation."); System.out.println("Licensed under the Apache 2.0 License"); System.out.println(); return null; } } public static class DefaultInfoCommand extends InfoCommand { @Override public Void call() throws Exception { super.call(); System.out.println("ERROR: No command specified."); System.out.println(); throw new ParseException("No command specified."); } } /** method intended for overriding when the script filename is different * @return the name of the script the user has invoked */ protected String cliScriptName() { return "brooklyn"; } /** * Build the commands. */ protected abstract CliBuilder cliBuilder(); protected void execCli(String ...args) { execCli(cliBuilder().build(), args); } protected void execCli(Cli parser, String ...args) { try { log.debug("Parsing command line arguments: {}", Arrays.asList(args)); BrooklynCommand command = parser.parse(args); log.debug("Executing command: {}", command); command.call(); System.exit(SUCCESS); } catch (ParseException pe) { // looks like the user typed it wrong System.err.println("Parse error: " + pe.getMessage()); // display // error System.err.println(getUsageInfo(parser)); // display cli help System.exit(PARSE_ERROR); } catch (FatalConfigurationRuntimeException e) { log.error("Configuration error: "+e.getMessage(), e.getCause()); System.err.println("Configuration error: " + e.getMessage()); System.exit(CONFIGURATION_ERROR); } catch (FatalRuntimeException e) { // anticipated non-configuration error log.error("Startup error: "+e.getMessage(), e.getCause()); System.err.println("Startup error: "+e.getMessage()); System.exit(EXECUTION_ERROR); } catch (Exception e) { // unexpected error during command execution log.error("Execution error: " + e.getMessage(), e); System.err.println("Execution error: " + e.getMessage()); if (!(e instanceof UserFacingException)) e.printStackTrace(); System.exit(EXECUTION_ERROR); } } protected String getUsageInfo(Cli parser) { StringBuilder help = new StringBuilder(); help.append("\n"); Help.help(parser.getMetadata(), Collections.emptyList(), help); return help.toString(); } }





  • © 2015 - 2024 Weber Informatics LLC | Privacy Policy