http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/main/java/brooklyn/cli/Main.java ---------------------------------------------------------------------- diff --git a/usage/cli/src/main/java/brooklyn/cli/Main.java b/usage/cli/src/main/java/brooklyn/cli/Main.java deleted file mode 100644 index 53aeb78..0000000 --- a/usage/cli/src/main/java/brooklyn/cli/Main.java +++ /dev/null @@ -1,986 +0,0 @@ -/* - * 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 brooklyn.cli; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.io.Console; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.Collection; -import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Objects.ToStringHelper; -import com.google.common.base.Stopwatch; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; - -import brooklyn.BrooklynVersion; -import brooklyn.basic.BrooklynTypes; -import org.apache.brooklyn.catalog.BrooklynCatalog; -import org.apache.brooklyn.catalog.CatalogItem; -import org.apache.brooklyn.catalog.CatalogItem.CatalogItemType; -import brooklyn.catalog.internal.CatalogInitialization; -import brooklyn.cli.CloudExplorer.BlobstoreGetBlobCommand; -import brooklyn.cli.CloudExplorer.BlobstoreListContainerCommand; -import brooklyn.cli.CloudExplorer.BlobstoreListContainersCommand; -import brooklyn.cli.CloudExplorer.ComputeDefaultTemplateCommand; -import brooklyn.cli.CloudExplorer.ComputeGetImageCommand; -import brooklyn.cli.CloudExplorer.ComputeListHardwareProfilesCommand; -import brooklyn.cli.CloudExplorer.ComputeListImagesCommand; -import brooklyn.cli.CloudExplorer.ComputeListInstancesCommand; -import brooklyn.cli.CloudExplorer.ComputeTerminateInstancesCommand; -import brooklyn.cli.ItemLister.ListAllCommand; -import brooklyn.entity.Application; -import brooklyn.entity.Entity; -import brooklyn.entity.basic.AbstractApplication; -import brooklyn.entity.basic.AbstractEntity; -import brooklyn.entity.basic.ApplicationBuilder; -import brooklyn.entity.basic.Entities; -import brooklyn.entity.basic.StartableApplication; -import brooklyn.entity.proxying.EntitySpec; -import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils; -import brooklyn.entity.rebind.persister.PersistMode; -import brooklyn.entity.rebind.transformer.CompoundTransformer; -import brooklyn.entity.trait.Startable; -import brooklyn.launcher.BrooklynLauncher; -import brooklyn.launcher.BrooklynServerDetails; -import brooklyn.launcher.config.StopWhichAppsOnShutdown; -import brooklyn.management.ManagementContext; -import brooklyn.management.Task; -import brooklyn.management.ha.HighAvailabilityMode; -import brooklyn.management.ha.OsgiManager; -import brooklyn.rest.security.PasswordHasher; -import brooklyn.rest.util.ShutdownHandler; -import brooklyn.util.ResourceUtils; -import brooklyn.util.exceptions.Exceptions; -import brooklyn.util.exceptions.FatalConfigurationRuntimeException; -import brooklyn.util.exceptions.FatalRuntimeException; -import brooklyn.util.exceptions.UserFacingException; -import brooklyn.util.guava.Maybe; -import brooklyn.util.javalang.Enums; -import brooklyn.util.net.Networking; -import brooklyn.util.text.Identifiers; -import brooklyn.util.text.StringEscapes.JavaStringEscapes; -import brooklyn.util.text.Strings; -import brooklyn.util.time.Duration; -import groovy.lang.GroovyClassLoader; -import groovy.lang.GroovyShell; -import io.airlift.command.Cli; -import io.airlift.command.Cli.CliBuilder; -import io.airlift.command.Command; -import io.airlift.command.Option; - -/** - * This class is the primary CLI for brooklyn. - * Run with the `help` argument for help. - * <p> - * This class is designed for subclassing, with subclasses typically: - * <li> providing their own static {@link #main(String...)} (of course) which need simply invoke - * {@link #execCli(String[])} with the arguments - * <li> returning their CLI name (e.g. "start.sh") in an overridden {@link #cliScriptName()} - * <li> providing an overridden {@link LaunchCommand} via {@link #cliLaunchCommand()} if desired - * <li> providing any other CLI customisations by overriding {@link #cliBuilder()} - * (typically calling the parent and then customizing the builder) - * <li> populating a custom catalog using {@link LaunchCommand#populateCatalog(BrooklynCatalog)} - */ -public class Main extends AbstractMain { - - private static final Logger log = LoggerFactory.getLogger(Main.class); - - public static void main(String... args) { - log.debug("Launching Brooklyn via CLI, with "+Arrays.toString(args)); - BrooklynVersion.INSTANCE.logSummary(); - new Main().execCli(args); - } - - @Command(name = "generate-password", description = "Generates a hashed web-console password") - public static class GeneratePasswordCommand extends BrooklynCommandCollectingArgs { - - @Option(name = { "--user" }, title = "username", required = true) - public String user; - - @Option(name = { "--stdin" }, title = "read password from stdin, instead of console", - description = "Before using stdin, read http://stackoverflow.com/a/715681/1393883 for discussion of security!") - public boolean useStdin; - - @Override - public Void call() throws Exception { - checkCanReadPassword(); - - System.out.print("Enter password: "); - System.out.flush(); - String password = readPassword(); - if (Strings.isBlank(password)) { - throw new UserFacingException("Password must not be blank; aborting"); - } - - System.out.print("Re-enter password: "); - System.out.flush(); - String password2 = readPassword(); - if (!password.equals(password2)) { - throw new UserFacingException("Passwords did not match; aborting"); - } - - String salt = Identifiers.makeRandomId(4); - String sha256password = PasswordHasher.sha256(salt, new String(password)); - - System.out.println(); - System.out.println("Please add the following to your brooklyn.properties:"); - System.out.println(); - System.out.println("brooklyn.webconsole.security.users="+user); - System.out.println("brooklyn.webconsole.security.user."+user+".salt="+salt); - System.out.println("brooklyn.webconsole.security.user."+user+".sha256="+sha256password); - - return null; - } - - private void checkCanReadPassword() { - if (useStdin) { - // yes; always - } else { - Console console = System.console(); - if (console == null) { - throw new FatalConfigurationRuntimeException("No console; cannot get password securely; aborting"); - } - } - } - - private String readPassword() throws IOException { - if (useStdin) { - return readLine(System.in); - } else { - return new String(System.console().readPassword()); - } - } - - private String readLine(InputStream in) throws IOException { - StringBuilder result = new StringBuilder(); - char c; - while ((c = (char)in.read()) != '\n') { - result.append(c); - } - return result.toString(); - } - } - - @Command(name = "launch", description = "Starts a server, optionally with applications") - public static class LaunchCommand extends BrooklynCommandCollectingArgs { - - @Option(name = { "--localBrooklynProperties" }, title = "local brooklyn.properties file", - description = "Load the given properties file, specific to this launch (appending to and overriding global properties)") - public String localBrooklynProperties; - - @Option(name = { "--noGlobalBrooklynProperties" }, title = "do not use any global brooklyn.properties file found", - description = "Do not use the default global brooklyn.properties file found") - public boolean noGlobalBrooklynProperties = false; - - @Option(name = { "-a", "--app" }, title = "application class or file", - description = "The Application to start. " + - "For example, my.AppName, file://my/app.yaml, or classpath://my/AppName.groovy -- " - + "note that a BROOKLYN_CLASSPATH environment variable may be required to " - + "load classes from other locations") - public String app; - - @Beta - @Option(name = { "-s", "--script" }, title = "script URI", - description = "EXPERIMENTAL. URI for a Groovy script to parse and load." + - " This script will run before starting the app.") - public String script = null; - - @Option(name = { "-l", "--location", "--locations" }, title = "location list", - description = "Specifies the locations where the application will be launched. " + - "You can specify more than one location as a comma-separated list of values " + - "(or as a JSON array, if the values are complex)") - public String locations; - - @Option(name = { "--catalogInitial" }, title = "catalog initial bom URI", - description = "Specifies a catalog.bom URI to be used to populate the initial catalog, " - + "loaded on first run, or when persistence is off/empty or the catalog is reset") - public String catalogInitial; - - @Option(name = { "--catalogReset" }, - description = "Specifies that any catalog items which have been persisted should be cleared") - public boolean catalogReset; - - @Option(name = { "--catalogAdd" }, title = "catalog bom URI to add", - description = "Specifies a catalog.bom to be added to the catalog") - public String catalogAdd; - - @Option(name = { "--catalogForce" }, - description = "Specifies that catalog items added via the CLI should be forcibly added, " - + "replacing any identical versions already registered (use with care!)") - public boolean catalogForce; - - @Option(name = { "-p", "--port" }, title = "port number", - description = "Use this port for the brooklyn management web console and REST API; " - + "default is 8081+ for http, 8443+ for https.") - public String port; - - @Option(name = { "--https" }, - description = "Launch the web console on https") - public boolean useHttps = false; - - @Option(name = { "-nc", "--noConsole" }, - description = "Do not start the web console or REST API") - public boolean noConsole = false; - - @Option(name = { "-b", "--bindAddress" }, - description = "Specifies the IP address of the NIC to bind the Brooklyn Management Console to") - public String bindAddress = null; - - @Option(name = { "-pa", "--publicAddress" }, - description = "Specifies the IP address or hostname that the Brooklyn Management Console will be available on") - public String publicAddress = null; - - @Option(name = { "--noConsoleSecurity" }, - description = "Whether to disable authentication and security filters for the web console (for use when debugging on a secure network or bound to localhost)") - public Boolean noConsoleSecurity = false; - - @Option(name = { "--startupContinueOnWebErrors" }, - description = "Continue on web subsystem failures during startup " - + "(default is to abort if the web API fails to start, as management access is not normally possible)") - public boolean startupContinueOnWebErrors = false; - - @Option(name = { "--startupFailOnPersistenceErrors" }, - description = "Fail on persistence/HA subsystem failures during startup " - + "(default is to continue, so errors can be viewed via the API)") - public boolean startupFailOnPersistenceErrors = false; - - @Option(name = { "--startupFailOnCatalogErrors" }, - description = "Fail on catalog subsystem failures during startup " - + "(default is to continue, so errors can be viewed via the API)") - public boolean startupFailOnCatalogErrors = false; - - @Option(name = { "--startupFailOnManagedAppsErrors" }, - description = "Fail startup on errors deploying of managed apps specified via the command line " - + "(default is to continue, so errors can be viewed via the API)") - public boolean startupFailOnManagedAppsErrors = false; - - @Beta - @Option(name = { "--startBrooklynNode" }, - description = "Start a BrooklynNode entity representing this Brooklyn instance") - public boolean startBrooklynNode = false; - - // Note in some cases, you can get java.util.concurrent.RejectedExecutionException - // if shutdown is not co-ordinated, looks like: {@linktourl https://gist.github.com/47066f72d6f6f79b953e} - @Beta - @Option(name = { "-sk", "--stopOnKeyPress" }, - description = "Shutdown immediately on user text entry after startup (useful for debugging and demos)") - public boolean stopOnKeyPress = false; - - final static String STOP_WHICH_APPS_ON_SHUTDOWN = "--stopOnShutdown"; - protected final static String STOP_ALL = "all"; - protected final static String STOP_ALL_IF_NOT_PERSISTED = "allIfNotPersisted"; - protected final static String STOP_NONE = "none"; - protected final static String STOP_THESE = "these"; - protected final static String STOP_THESE_IF_NOT_PERSISTED = "theseIfNotPersisted"; - static { Enums.checkAllEnumeratedIgnoreCase(StopWhichAppsOnShutdown.class, STOP_ALL, STOP_ALL_IF_NOT_PERSISTED, STOP_NONE, STOP_THESE, STOP_THESE_IF_NOT_PERSISTED); } - - @Option(name = { STOP_WHICH_APPS_ON_SHUTDOWN }, - allowedValues = { STOP_ALL, STOP_ALL_IF_NOT_PERSISTED, STOP_NONE, STOP_THESE, STOP_THESE_IF_NOT_PERSISTED }, - description = "Which managed applications to stop on shutdown. Possible values are:\n"+ - "all: stop all apps\n"+ - "none: leave all apps running\n"+ - "these: stop the apps explicitly started on this command line, but leave others started subsequently running\n"+ - "theseIfNotPersisted: stop the apps started on this command line IF persistence is not enabled, otherwise leave all running\n"+ - "allIfNotPersisted: stop all apps IF persistence is not enabled, otherwise leave all running") - public String stopWhichAppsOnShutdown = STOP_THESE_IF_NOT_PERSISTED; - - @Option(name = { "--exitAndLeaveAppsRunningAfterStarting" }, - description = "Once the application to start (from --app) is running exit the process, leaving any entities running. " - + "Can be used in combination with --persist auto --persistenceDir <custom folder location> to attach to the running app at a later time.") - public boolean exitAndLeaveAppsRunningAfterStarting = false; - - final static String PERSIST_OPTION = "--persist"; - protected final static String PERSIST_OPTION_DISABLED = "disabled"; - protected final static String PERSIST_OPTION_AUTO = "auto"; - protected final static String PERSIST_OPTION_REBIND = "rebind"; - protected final static String PERSIST_OPTION_CLEAN = "clean"; - static { Enums.checkAllEnumeratedIgnoreCase(PersistMode.class, PERSIST_OPTION_DISABLED, PERSIST_OPTION_AUTO, PERSIST_OPTION_REBIND, PERSIST_OPTION_CLEAN); } - - // TODO currently defaults to disabled; want it to default to on, when we're ready - // TODO how to force a line-split per option?! - // Looks like java.io.airlift.airline.UsagePrinter is splitting the description by word, and - // wrapping it automatically. - // See https://github.com/airlift/airline/issues/30 - @Option(name = { PERSIST_OPTION }, - allowedValues = { PERSIST_OPTION_DISABLED, PERSIST_OPTION_AUTO, PERSIST_OPTION_REBIND, PERSIST_OPTION_CLEAN }, - title = "persistence mode", - description = - "The persistence mode. Possible values are: \n"+ - "disabled: will not read or persist any state; \n"+ - "auto: will rebind to any existing state, or start up fresh if no state; \n"+ - "rebind: will rebind to the existing state, or fail if no state available; \n"+ - "clean: will start up fresh (removing any existing state)") - public String persist = PERSIST_OPTION_DISABLED; - - @Option(name = { "--persistenceDir" }, title = "persistence dir", - description = "The directory to read/write persisted state (or container name if using an object store)") - public String persistenceDir; - - @Option(name = { "--persistenceLocation" }, title = "persistence location", - description = "The location spec for an object store to read/write persisted state") - public String persistenceLocation; - - final static String HA_OPTION = "--highAvailability"; - protected final static String HA_OPTION_DISABLED = "disabled"; - protected final static String HA_OPTION_AUTO = "auto"; - protected final static String HA_OPTION_MASTER = "master"; - protected final static String HA_OPTION_STANDBY = "standby"; - protected final static String HA_OPTION_HOT_STANDBY = "hot_standby"; - protected final static String HA_OPTION_HOT_BACKUP = "hot_backup"; - static { Enums.checkAllEnumeratedIgnoreCase(HighAvailabilityMode.class, HA_OPTION_AUTO, HA_OPTION_DISABLED, HA_OPTION_MASTER, HA_OPTION_STANDBY, HA_OPTION_HOT_STANDBY, HA_OPTION_HOT_BACKUP); } - - @Option(name = { HA_OPTION }, allowedValues = { HA_OPTION_DISABLED, HA_OPTION_AUTO, HA_OPTION_MASTER, HA_OPTION_STANDBY, HA_OPTION_HOT_STANDBY, HA_OPTION_HOT_BACKUP }, - title = "high availability mode", - description = - "The high availability mode. Possible values are: \n"+ - "disabled: management node works in isolation - will not cooperate with any other standby/master nodes in management plane; \n"+ - "auto: will look for other management nodes, and will allocate itself as standby or master based on other nodes' states; \n"+ - "master: will startup as master - if there is already a master then fails immediately; \n"+ - "standby: will start up as lukewarm standby with no state - if there is not already a master then fails immediately, " - + "and if there is a master which subsequently fails, this node can promote itself; \n"+ - "hot_standby: will start up as hot standby in read-only mode - if there is not already a master then fails immediately, " - + "and if there is a master which subseuqently fails, this node can promote itself; \n"+ - "hot_backup: will start up as hot backup in read-only mode - no master is required, and this node will not become a master" - ) - public String highAvailability = HA_OPTION_AUTO; - - @VisibleForTesting - protected ManagementContext explicitManagementContext; - - @Override - public Void call() throws Exception { - // Configure launcher - BrooklynLauncher launcher; - AppShutdownHandler shutdownHandler = new AppShutdownHandler(); - failIfArguments(); - try { - if (log.isDebugEnabled()) log.debug("Invoked launch command {}", this); - - if (!quiet) stdout.println(banner); - - if (verbose) { - if (app != null) { - stdout.println("Launching brooklyn app: " + app + " in " + locations); - } else { - stdout.println("Launching brooklyn server (no app)"); - } - } - - PersistMode persistMode = computePersistMode(); - HighAvailabilityMode highAvailabilityMode = computeHighAvailabilityMode(persistMode); - - StopWhichAppsOnShutdown stopWhichAppsOnShutdownMode = computeStopWhichAppsOnShutdown(); - - computeLocations(); - - ResourceUtils utils = ResourceUtils.create(this); - GroovyClassLoader loader = new GroovyClassLoader(getClass().getClassLoader()); - - // First, run a setup script if the user has provided one - if (script != null) { - execGroovyScript(utils, loader, script); - } - - launcher = createLauncher(); - - CatalogInitialization catInit = new CatalogInitialization(catalogInitial, catalogReset, catalogAdd, catalogForce); - catInit.addPopulationCallback(new Function<CatalogInitialization,Void>() { - @Override - public Void apply(CatalogInitialization catInit) { - try { - populateCatalog(catInit.getManagementContext().getCatalog()); - } catch (Throwable e) { - catInit.handleException(e, "overridden main class populate catalog"); - } - - // Force load of catalog (so web console is up to date) - confirmCatalog(catInit); - return null; - } - }); - catInit.setFailOnStartupErrors(startupFailOnCatalogErrors); - launcher.catalogInitialization(catInit); - - launcher.persistMode(persistMode); - launcher.persistenceDir(persistenceDir); - launcher.persistenceLocation(persistenceLocation); - - launcher.highAvailabilityMode(highAvailabilityMode); - - launcher.stopWhichAppsOnShutdown(stopWhichAppsOnShutdownMode); - launcher.shutdownHandler(shutdownHandler); - - computeAndSetApp(launcher, utils, loader); - - customize(launcher); - - } catch (FatalConfigurationRuntimeException e) { - throw e; - } catch (Exception e) { - throw new FatalConfigurationRuntimeException("Fatal error configuring Brooklyn launch: "+e.getMessage(), e); - } - - // Launch server - try { - launcher.start(); - } catch (FatalRuntimeException e) { - // rely on caller logging this propagated exception - throw e; - } catch (Exception e) { - // for other exceptions we log it, possibly redundantly but better too much than too little - Exceptions.propagateIfFatal(e); - log.error("Error launching brooklyn: "+Exceptions.collapseText(e), e); - try { - launcher.terminate(); - } catch (Exception e2) { - log.warn("Subsequent error during termination: "+e2); - log.debug("Details of subsequent error during termination: "+e2, e2); - } - Exceptions.propagate(e); - } - - BrooklynServerDetails server = launcher.getServerDetails(); - ManagementContext ctx = server.getManagementContext(); - - if (verbose) { - Entities.dumpInfo(launcher.getApplications()); - } - - if (!exitAndLeaveAppsRunningAfterStarting) { - waitAfterLaunch(ctx, shutdownHandler); - } - - // will call mgmt.terminate() in BrooklynShutdownHookJob - return null; - } - - /** can be overridden by subclasses which need to customize the launcher and/or management */ - protected void customize(BrooklynLauncher launcher) { - } - - protected void computeLocations() { - boolean hasLocations = !Strings.isBlank(locations); - if (app != null) { - if (hasLocations && isYamlApp()) { - log.info("YAML app combined with command line locations; YAML locations will take precedence; this behaviour may change in subsequent versions"); - } else if (!hasLocations && isYamlApp()) { - log.info("No locations supplied; defaulting to locations defined in YAML (if any)"); - } else if (!hasLocations) { - log.info("No locations supplied; starting with no locations"); - } - } else if (hasLocations) { - log.error("Locations specified without any applications; ignoring locations"); - } - } - - protected boolean isYamlApp() { - return app != null && app.endsWith(".yaml"); - } - - protected PersistMode computePersistMode() { - Maybe<PersistMode> persistMode = Enums.valueOfIgnoreCase(PersistMode.class, persist); - if (!persistMode.isPresent()) { - if (Strings.isBlank(persist)) { - throw new FatalConfigurationRuntimeException("Persist mode must not be blank"); - } else { - throw new FatalConfigurationRuntimeException("Illegal persist setting: "+persist); - } - } - - if (persistMode.get() == PersistMode.DISABLED) { - if (Strings.isNonBlank(persistenceDir)) - throw new FatalConfigurationRuntimeException("Cannot specify persistenceDir when persist is disabled"); - if (Strings.isNonBlank(persistenceLocation)) - throw new FatalConfigurationRuntimeException("Cannot specify persistenceLocation when persist is disabled"); - } - return persistMode.get(); - } - - protected HighAvailabilityMode computeHighAvailabilityMode(PersistMode persistMode) { - Maybe<HighAvailabilityMode> highAvailabilityMode = Enums.valueOfIgnoreCase(HighAvailabilityMode.class, highAvailability); - if (!highAvailabilityMode.isPresent()) { - if (Strings.isBlank(highAvailability)) { - throw new FatalConfigurationRuntimeException("High availability mode must not be blank"); - } else { - throw new FatalConfigurationRuntimeException("Illegal highAvailability setting: "+highAvailability); - } - } - - if (highAvailabilityMode.get() != HighAvailabilityMode.DISABLED) { - if (persistMode == PersistMode.DISABLED) { - if (highAvailabilityMode.get() == HighAvailabilityMode.AUTO) - return HighAvailabilityMode.DISABLED; - throw new FatalConfigurationRuntimeException("Cannot specify highAvailability when persistence is disabled"); - } else if (persistMode == PersistMode.CLEAN && - (highAvailabilityMode.get() == HighAvailabilityMode.STANDBY - || highAvailabilityMode.get() == HighAvailabilityMode.HOT_STANDBY - || highAvailabilityMode.get() == HighAvailabilityMode.HOT_BACKUP)) { - throw new FatalConfigurationRuntimeException("Cannot specify highAvailability "+highAvailabilityMode.get()+" when persistence is CLEAN"); - } - } - return highAvailabilityMode.get(); - } - - protected StopWhichAppsOnShutdown computeStopWhichAppsOnShutdown() { - boolean isDefault = STOP_THESE_IF_NOT_PERSISTED.equals(stopWhichAppsOnShutdown); - if (exitAndLeaveAppsRunningAfterStarting && isDefault) { - return StopWhichAppsOnShutdown.NONE; - } else { - return Enums.valueOfIgnoreCase(StopWhichAppsOnShutdown.class, stopWhichAppsOnShutdown).get(); - } - } - - @VisibleForTesting - /** forces the launcher to use the given management context, when programmatically invoked; - * mainly used when testing to inject a safe (and fast) mgmt context */ - public void useManagementContext(ManagementContext mgmt) { - explicitManagementContext = mgmt; - } - - protected BrooklynLauncher createLauncher() { - BrooklynLauncher launcher; - launcher = BrooklynLauncher.newInstance(); - launcher.localBrooklynPropertiesFile(localBrooklynProperties) - .ignorePersistenceErrors(!startupFailOnPersistenceErrors) - .ignoreCatalogErrors(!startupFailOnCatalogErrors) - .ignoreWebErrors(startupContinueOnWebErrors) - .ignoreAppErrors(!startupFailOnManagedAppsErrors) - .locations(Strings.isBlank(locations) ? ImmutableList.<String>of() : JavaStringEscapes.unwrapJsonishListIfPossible(locations)); - - launcher.webconsole(!noConsole); - if (useHttps) { - // true sets it; false (not set) leaves it blank and falls back to config key - // (no way currently to override config key, but that could be added) - launcher.webconsoleHttps(useHttps); - } - launcher.webconsolePort(port); - - if (noGlobalBrooklynProperties) { - log.debug("Configuring to disable global brooklyn.properties"); - launcher.globalBrooklynPropertiesFile(null); - } - if (noConsoleSecurity) { - log.info("Configuring to disable console security"); - launcher.installSecurityFilter(false); - } - if (startBrooklynNode) { - log.info("Configuring BrooklynNode entity startup"); - launcher.startBrooklynNode(true); - } - if (Strings.isNonEmpty(bindAddress)) { - log.debug("Configuring bind address as "+bindAddress); - launcher.bindAddress(Networking.getInetAddressWithFixedName(bindAddress)); - } - if (Strings.isNonEmpty(publicAddress)) { - log.debug("Configuring public address as "+publicAddress); - launcher.publicAddress(Networking.getInetAddressWithFixedName(publicAddress)); - } - if (explicitManagementContext!=null) { - log.debug("Configuring explicit management context "+explicitManagementContext); - launcher.managementContext(explicitManagementContext); - } - return launcher; - } - - /** method intended for subclassing, to add custom items to the catalog */ - protected void populateCatalog(BrooklynCatalog catalog) { - // nothing else added here - } - - protected void confirmCatalog(CatalogInitialization catInit) { - // Force load of catalog (so web console is up to date) - Stopwatch time = Stopwatch.createStarted(); - BrooklynCatalog catalog = catInit.getManagementContext().getCatalog(); - Iterable<CatalogItem<Object, Object>> items = catalog.getCatalogItems(); - for (CatalogItem<Object, Object> item: items) { - try { - if (item.getCatalogItemType()==CatalogItemType.TEMPLATE) { - // skip validation of templates, they might contain instructions, - // and additionally they might contain multiple items in which case - // the validation below won't work anyway (you need to go via a deployment plan) - } else { - Object spec = catalog.createSpec(item); - if (spec instanceof EntitySpec) { - BrooklynTypes.getDefinedEntityType(((EntitySpec<?>)spec).getType()); - } - log.debug("Catalog loaded spec "+spec+" for item "+item); - } - } catch (Throwable throwable) { - catInit.handleException(throwable, item); - } - } - log.debug("Catalog (size "+Iterables.size(items)+") confirmed in "+Duration.of(time)); - // nothing else added here - } - - /** convenience for subclasses to specify that an app should run, - * throwing the right (caught) error if another app has already been specified */ - protected void setAppToLaunch(String className) { - if (app!=null) { - if (app.equals(className)) return; - throw new FatalConfigurationRuntimeException("Cannot specify app '"+className+"' when '"+app+"' is already specified; " - + "remove one or more conflicting CLI arguments."); - } - app = className; - } - - protected void computeAndSetApp(BrooklynLauncher launcher, ResourceUtils utils, GroovyClassLoader loader) - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { - if (app != null) { - // Create the instance of the brooklyn app - log.debug("Loading the user's application: {}", app); - - if (isYamlApp()) { - log.debug("Loading application as YAML spec: {}", app); - String content = utils.getResourceAsString(app); - launcher.application(content); - } else { - Object loadedApp = loadApplicationFromClasspathOrParse(utils, loader, app); - if (loadedApp instanceof ApplicationBuilder) { - launcher.application((ApplicationBuilder)loadedApp); - } else if (loadedApp instanceof Application) { - launcher.application((AbstractApplication)loadedApp); - } else { - throw new FatalConfigurationRuntimeException("Unexpected application type "+(loadedApp==null ? null : loadedApp.getClass())+", for app "+loadedApp); - } - } - } - } - - protected void waitAfterLaunch(ManagementContext ctx, AppShutdownHandler shutdownHandler) throws IOException { - if (stopOnKeyPress) { - // Wait for the user to type a key - log.info("Server started. Press return to stop."); - // Read in another thread so we can use timeout on the wait. - Task<Void> readTask = ctx.getExecutionManager().submit(new Callable<Void>() { - @Override - public Void call() throws Exception { - stdin.read(); - return null; - } - }); - while (!shutdownHandler.isRequested()) { - try { - readTask.get(Duration.ONE_SECOND); - break; - } catch (TimeoutException e) { - //check if there's a shutdown request - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw Exceptions.propagate(e); - } catch (ExecutionException e) { - throw Exceptions.propagate(e); - } - } - log.info("Shutting down applications."); - stopAllApps(ctx.getApplications()); - } else { - // Block forever so that Brooklyn doesn't exit (until someone does cntrl-c or kill) - log.info("Launched Brooklyn; will now block until shutdown command received via GUI/API (recommended) or process interrupt."); - shutdownHandler.waitOnShutdownRequest(); - } - } - - protected void execGroovyScript(ResourceUtils utils, GroovyClassLoader loader, String script) { - log.debug("Running the user provided script: {}", script); - String content = utils.getResourceAsString(script); - GroovyShell shell = new GroovyShell(loader); - shell.evaluate(content); - } - - /** - * Helper method that gets an instance of a brooklyn {@link AbstractApplication} or an {@link ApplicationBuilder}. - * Guaranteed to be non-null result of one of those types (throwing exception if app not appropriate). - */ - @SuppressWarnings("unchecked") - protected Object loadApplicationFromClasspathOrParse(ResourceUtils utils, GroovyClassLoader loader, String app) - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { - - Class<?> tempclazz; - log.debug("Loading application as class on classpath: {}", app); - try { - tempclazz = loader.loadClass(app, true, false); - } catch (ClassNotFoundException cnfe) { // Not a class on the classpath - log.debug("Loading \"{}\" as class on classpath failed, now trying as .groovy source file", app); - String content = utils.getResourceAsString(app); - tempclazz = loader.parseClass(content); - } - final Class<?> clazz = tempclazz; - - // Instantiate an app builder (wrapping app class in ApplicationBuilder, if necessary) - if (ApplicationBuilder.class.isAssignableFrom(clazz)) { - Constructor<?> constructor = clazz.getConstructor(); - return (ApplicationBuilder) constructor.newInstance(); - } else if (StartableApplication.class.isAssignableFrom(clazz)) { - EntitySpec<? extends StartableApplication> appSpec; - if (tempclazz.isInterface()) - appSpec = EntitySpec.create((Class<? extends StartableApplication>) clazz); - else - appSpec = EntitySpec.create(StartableApplication.class, (Class<? extends StartableApplication>) clazz); - return new ApplicationBuilder(appSpec) { - @Override protected void doBuild() { - }}; - } else if (AbstractApplication.class.isAssignableFrom(clazz)) { - // TODO If this application overrides init() then in trouble, as that won't get called! - // TODO grr; what to do about non-startable applications? - // without this we could return ApplicationBuilder rather than Object - Constructor<?> constructor = clazz.getConstructor(); - return (AbstractApplication) constructor.newInstance(); - } else if (AbstractEntity.class.isAssignableFrom(clazz)) { - // TODO Should we really accept any entity type, and just wrap it in an app? That's not documented! - return new ApplicationBuilder() { - @Override protected void doBuild() { - addChild(EntitySpec.create(Entity.class).impl((Class<? extends AbstractEntity>)clazz).additionalInterfaces(clazz.getInterfaces())); - }}; - } else if (Entity.class.isAssignableFrom(clazz)) { - return new ApplicationBuilder() { - @Override protected void doBuild() { - addChild(EntitySpec.create((Class<? extends Entity>)clazz)); - }}; - } else { - throw new FatalConfigurationRuntimeException("Application class "+clazz+" must extend one of ApplicationBuilder or AbstractApplication"); - } - } - - @VisibleForTesting - protected void stopAllApps(Collection<? extends Application> applications) { - for (Application application : applications) { - try { - if (application instanceof Startable) { - ((Startable)application).stop(); - } - } catch (Exception e) { - log.error("Error stopping "+application+": "+e, e); - } - } - } - - @Override - public ToStringHelper string() { - return super.string() - .add("app", app) - .add("script", script) - .add("location", locations) - .add("port", port) - .add("bindAddress", bindAddress) - .add("noConsole", noConsole) - .add("noConsoleSecurity", noConsoleSecurity) - .add("startupFailOnPersistenceErrors", startupFailOnPersistenceErrors) - .add("startupFailsOnCatalogErrors", startupFailOnCatalogErrors) - .add("startupContinueOnWebErrors", startupContinueOnWebErrors) - .add("startupFailOnManagedAppsErrors", startupFailOnManagedAppsErrors) - .add("catalogInitial", catalogInitial) - .add("catalogAdd", catalogAdd) - .add("catalogReset", catalogReset) - .add("catalogForce", catalogForce) - .add("stopWhichAppsOnShutdown", stopWhichAppsOnShutdown) - .add("stopOnKeyPress", stopOnKeyPress) - .add("localBrooklynProperties", localBrooklynProperties) - .add("persist", persist) - .add("persistenceLocation", persistenceLocation) - .add("persistenceDir", persistenceDir) - .add("highAvailability", highAvailability) - .add("exitAndLeaveAppsRunningAfterStarting", exitAndLeaveAppsRunningAfterStarting); - } - } - - @Command(name = "copy-state", description = "Retrieves persisted state") - public static class CopyStateCommand extends BrooklynCommandCollectingArgs { - - @Option(name = { "--localBrooklynProperties" }, title = "local brooklyn.properties file", - description = "local brooklyn.properties file, specific to this launch (appending to and overriding global properties)") - public String localBrooklynProperties; - - @Option(name = { "--persistenceDir" }, title = "persistence dir", - description = "The directory to read persisted state (or container name if using an object store)") - public String persistenceDir; - - @Option(name = { "--persistenceLocation" }, title = "persistence location", - description = "The location spec for an object store to read persisted state") - public String persistenceLocation; - - @Option(name = { "--destinationDir" }, required = true, title = "destination dir", - description = "The directory to copy persistence data to") - public String destinationDir; - - @Option(name = { "--destinationLocation" }, title = "persistence location", - description = "The location spec for an object store to copy data to") - public String destinationLocation; - - @Option(name = { "--transformations" }, title = "transformations", - description = "local transformations file, to be applied to the copy of the data before uploading it") - public String transformations; - - @Override - public Void call() throws Exception { - checkNotNull(destinationDir, "destinationDir"); // presumably because required=true this will never be null! - - // Configure launcher - BrooklynLauncher launcher; - failIfArguments(); - try { - log.info("Retrieving and copying persisted state to "+destinationDir+(Strings.isBlank(destinationLocation) ? "" : " @ "+destinationLocation)); - - if (!quiet) stdout.println(banner); - - PersistMode persistMode = PersistMode.AUTO; - HighAvailabilityMode highAvailabilityMode = HighAvailabilityMode.DISABLED; - - launcher = BrooklynLauncher.newInstance() - .localBrooklynPropertiesFile(localBrooklynProperties) - .brooklynProperties(OsgiManager.USE_OSGI, false) - .persistMode(persistMode) - .persistenceDir(persistenceDir) - .persistenceLocation(persistenceLocation) - .highAvailabilityMode(highAvailabilityMode); - - } catch (FatalConfigurationRuntimeException e) { - throw e; - } catch (Exception e) { - throw new FatalConfigurationRuntimeException("Fatal error configuring Brooklyn launch: "+e.getMessage(), e); - } - - try { - launcher.copyPersistedState(destinationDir, destinationLocation, loadTransformer(transformations)); - } catch (FatalRuntimeException e) { - // rely on caller logging this propagated exception - throw e; - } catch (Exception e) { - // for other exceptions we log it, possibly redundantly but better too much than too little - Exceptions.propagateIfFatal(e); - log.error("Error retrieving persisted state: "+Exceptions.collapseText(e), e); - Exceptions.propagate(e); - } finally { - try { - launcher.terminate(); - } catch (Exception e2) { - log.warn("Subsequent error during termination: "+e2); - log.debug("Details of subsequent error during termination: "+e2, e2); - } - } - - return null; - } - - protected CompoundTransformer loadTransformer(String transformationsFileUrl) { - return BrooklynPersistenceUtils.loadTransformer(ResourceUtils.create(this), transformationsFileUrl); - } - - @Override - public ToStringHelper string() { - return super.string() - .add("localBrooklynProperties", localBrooklynProperties) - .add("persistenceLocation", persistenceLocation) - .add("persistenceDir", persistenceDir) - .add("destinationDir", destinationDir); - } - } - - /** method intended for overriding when a different {@link Cli} is desired, - * or when the subclass wishes to change any of the arguments */ - @SuppressWarnings("unchecked") - @Override - protected CliBuilder<BrooklynCommand> cliBuilder() { - CliBuilder<BrooklynCommand> builder = Cli.<BrooklynCommand>builder(cliScriptName()) - .withDescription("Brooklyn Management Service") - .withDefaultCommand(cliDefaultInfoCommand()) - .withCommands( - HelpCommand.class, - cliInfoCommand(), - GeneratePasswordCommand.class, - CopyStateCommand.class, - ListAllCommand.class, - cliLaunchCommand() - ); - - builder.withGroup("cloud-compute") - .withDescription("Access compute details of a given cloud") - .withDefaultCommand(HelpCommand.class) - .withCommands( - ComputeListImagesCommand.class, - ComputeListHardwareProfilesCommand.class, - ComputeListInstancesCommand.class, - ComputeGetImageCommand.class, - ComputeDefaultTemplateCommand.class, - ComputeTerminateInstancesCommand.class); - - builder.withGroup("cloud-blobstore") - .withDescription("Access blobstore details of a given cloud") - .withDefaultCommand(HelpCommand.class) - .withCommands( - BlobstoreListContainersCommand.class, - BlobstoreListContainerCommand.class, - BlobstoreGetBlobCommand.class); - - return builder; - } - - /** method intended for overriding when a custom {@link LaunchCommand} is being specified */ - protected Class<? extends BrooklynCommand> cliLaunchCommand() { - return LaunchCommand.class; - } - - /** method intended for overriding when a custom {@link InfoCommand} is being specified */ - protected Class<? extends BrooklynCommand> cliInfoCommand() { - return InfoCommand.class; - } - - /** method intended for overriding when a custom {@link InfoCommand} is being specified */ - protected Class<? extends BrooklynCommand> cliDefaultInfoCommand() { - return DefaultInfoCommand.class; - } - - public static class AppShutdownHandler implements ShutdownHandler { - private CountDownLatch lock = new CountDownLatch(1); - - @Override - public void onShutdownRequest() { - lock.countDown(); - } - - public boolean isRequested() { - return lock.getCount() == 0; - } - - public void waitOnShutdownRequest() { - try { - lock.await(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; // exit gracefully - } - } - } -}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java ---------------------------------------------------------------------- diff --git a/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java b/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java deleted file mode 100644 index 2fb913b..0000000 --- a/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * 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 brooklyn.cli.lister; - -import java.io.File; -import java.lang.annotation.Annotation; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import org.reflections.Reflections; -import org.reflections.scanners.FieldAnnotationsScanner; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; -import org.reflections.util.ConfigurationBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.basic.BrooklynObject; -import brooklyn.enricher.basic.AbstractEnricher; -import brooklyn.entity.Application; -import brooklyn.entity.Entity; -import brooklyn.entity.basic.AbstractApplication; -import brooklyn.entity.basic.AbstractEntity; -import brooklyn.entity.basic.SoftwareProcessImpl; -import brooklyn.policy.Enricher; -import brooklyn.policy.Policy; -import brooklyn.policy.basic.AbstractPolicy; -import brooklyn.util.ResourceUtils; -import brooklyn.util.javalang.UrlClassLoader; -import brooklyn.util.net.Urls; -import brooklyn.util.os.Os; - -import com.google.common.annotations.Beta; -import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; - -public class ClassFinder { - - private static final Logger log = LoggerFactory.getLogger(ClassFinder.class); - - private static final Collection<Class<?>> BORING = ImmutableList.<Class<?>>of( - Entity.class, - AbstractEntity.class, - SoftwareProcessImpl.class, - Application.class, - AbstractApplication.class, - Policy.class, - Enricher.class, - AbstractPolicy.class, - AbstractEnricher.class); - - public static Predicate<Class<?>> notBoring() { - return new Predicate<Class<?>>() { - public boolean apply(Class<?> input) { - return (input != null && !BORING.contains(input)); - } - }; - } - - public static Predicate<Class<?>> withAnnotation(final Class<? extends Annotation> annotation) { - return new Predicate<Class<?>>() { - public boolean apply(Class<?> input) { - return (input != null && input.getAnnotation(annotation) != null); - } - }; - } - - public static Predicate<? super Class<? extends BrooklynObject>> withClassNameMatching(final String typeRegex) { - return new Predicate<Class<?>>() { - public boolean apply(Class<?> input) { - return (input != null && input.getName() != null && input.getName().matches(typeRegex)); - } - }; - } - - /** finds a jar at a url, or for directories, jars under a path */ - @Beta - public static List<URL> toJarUrls(String url) throws MalformedURLException { - if (url==null) throw new NullPointerException("Cannot read from null"); - if (url=="") throw new NullPointerException("Cannot read from empty string"); - - List<URL> result = Lists.newArrayList(); - - String protocol = Urls.getProtocol(url); - if (protocol!=null) { - // it's a URL - easy - if ("file".equals(protocol)) { - url = ResourceUtils.tidyFileUrl(url); - } - result.add(new URL(url)); - } else { - // treat as file - String tidiedPath = Os.tidyPath(url); - File tidiedFile = new File(tidiedPath); - if (tidiedFile.isDirectory()) { - List<File> toscan = Lists.newLinkedList(); - toscan.add(tidiedFile); - while (toscan.size() > 0) { - File file = toscan.remove(0); - if (file.isFile()) { - if (file.getName().toLowerCase().endsWith(".jar")) { - result.add(new URL("file://"+file.getAbsolutePath())); - } - } else if (file.isDirectory()) { - for (File subfile : file.listFiles()) { - toscan.add(subfile); - } - } else { - log.info("Cannot read "+file+"; not a file or directory"); - } - } - } else { - result.add(tidiedFile.toURI().toURL()); - } - } - - return result; - } - - public static <T extends BrooklynObject> Set<Class<? extends T>> findClasses(Collection<URL> urls, Class<T> clazz) { - ClassLoader classLoader = new UrlClassLoader(urls.toArray(new URL[urls.size()])); - - Reflections reflections = new ConfigurationBuilder() - .addClassLoader(classLoader) - .addScanners(new SubTypesScanner(), new TypeAnnotationsScanner(), new FieldAnnotationsScanner()) - .addUrls(urls) - .build(); - - Set<Class<? extends T>> types = reflections.getSubTypesOf(clazz); - - return types; - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java ---------------------------------------------------------------------- diff --git a/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java b/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java deleted file mode 100644 index 5ffd57d..0000000 --- a/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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 brooklyn.cli.lister; - -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.basic.BrooklynDynamicType; -import brooklyn.basic.BrooklynObject; -import brooklyn.basic.BrooklynType; -import brooklyn.basic.BrooklynTypes; -import org.apache.brooklyn.catalog.Catalog; -import brooklyn.config.ConfigKey; -import brooklyn.entity.Effector; -import brooklyn.entity.EntityType; -import brooklyn.entity.basic.BrooklynConfigKeys; -import brooklyn.event.Sensor; -import brooklyn.location.LocationResolver; -import brooklyn.rest.domain.EffectorSummary; -import brooklyn.rest.domain.EntityConfigSummary; -import brooklyn.rest.domain.SensorSummary; -import brooklyn.rest.domain.SummaryComparators; -import brooklyn.rest.transform.EffectorTransformer; -import brooklyn.rest.transform.EntityTransformer; -import brooklyn.rest.transform.SensorTransformer; -import brooklyn.util.exceptions.RuntimeInterruptedException; - -import com.google.common.base.Strings; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -public class ItemDescriptors { - - private static final Logger LOG = LoggerFactory.getLogger(ItemDescriptors.class); - - public static List<Map<String, Object>> toItemDescriptors(Iterable<? extends Class<? extends BrooklynObject>> types, boolean headingsOnly) { - return toItemDescriptors(types, headingsOnly, null); - } - - public static List<Map<String, Object>> toItemDescriptors(Iterable<? extends Class<? extends BrooklynObject>> types, boolean headingsOnly, final String sortField) { - List<Map<String, Object>> itemDescriptors = Lists.newArrayList(); - - for (Class<? extends BrooklynObject> type : types) { - try { - Map<String, Object> itemDescriptor = toItemDescriptor(type, headingsOnly); - itemDescriptors.add(itemDescriptor); - } catch (Throwable throwable) { - if (throwable instanceof InterruptedException) - throw new RuntimeInterruptedException((InterruptedException) throwable); - if (throwable instanceof RuntimeInterruptedException) - throw (RuntimeInterruptedException) throwable; - - LOG.warn("Could not load "+type+": "+throwable); - } - } - - if (!Strings.isNullOrEmpty(sortField)) { - Collections.sort(itemDescriptors, new Comparator<Map<String, Object>>() { - @Override public int compare(Map<String, Object> id1, Map<String, Object> id2) { - Object o1 = id1.get(sortField); - Object o2 = id2.get(sortField); - if (o1 == null) { - return o2 == null ? 0 : 1; - } - if (o2 == null) { - return -1; - } - return o1.toString().compareTo(o2.toString()); - } - }); - } - - return itemDescriptors; - } - - public static Map<String,Object> toItemDescriptor(Class<? extends BrooklynObject> clazz, boolean headingsOnly) { - BrooklynDynamicType<?, ?> dynamicType = BrooklynTypes.getDefinedBrooklynType(clazz); - BrooklynType type = dynamicType.getSnapshot(); - ConfigKey<?> version = dynamicType.getConfigKey(BrooklynConfigKeys.SUGGESTED_VERSION.getName()); - - Map<String,Object> result = Maps.newLinkedHashMap(); - - result.put("type", clazz.getName()); - if (version != null) { - result.put("defaultVersion", version.getDefaultValue()); - } - - Catalog catalogAnnotation = clazz.getAnnotation(Catalog.class); - if (catalogAnnotation != null) { - result.put("name", catalogAnnotation.name()); - result.put("description", catalogAnnotation.description()); - result.put("iconUrl", catalogAnnotation.iconUrl()); - } - - Deprecated deprecatedAnnotation = clazz.getAnnotation(Deprecated.class); - if (deprecatedAnnotation != null) { - result.put("deprecated", true); - } - - if (!headingsOnly) { - Set<EntityConfigSummary> config = Sets.newTreeSet(SummaryComparators.nameComparator()); - Set<SensorSummary> sensors = Sets.newTreeSet(SummaryComparators.nameComparator()); - Set<EffectorSummary> effectors = Sets.newTreeSet(SummaryComparators.nameComparator()); - - for (ConfigKey<?> x: type.getConfigKeys()) { - config.add(EntityTransformer.entityConfigSummary(x, dynamicType.getConfigKeyField(x.getName()))); - } - result.put("config", config); - - if (type instanceof EntityType) { - for (Sensor<?> x: ((EntityType)type).getSensors()) - sensors.add(SensorTransformer.sensorSummaryForCatalog(x)); - result.put("sensors", sensors); - - for (Effector<?> x: ((EntityType)type).getEffectors()) - effectors.add(EffectorTransformer.effectorSummaryForCatalog(x)); - result.put("effectors", effectors); - } - } - - return result; - } - - public static Object toItemDescriptors(List<LocationResolver> resolvers) { - return toItemDescriptors(resolvers, false); - } - - public static Object toItemDescriptors(List<LocationResolver> resolvers, Boolean sort) { - List<Object> result = Lists.newArrayList(); - for (LocationResolver resolver : resolvers) { - result.add(toItemDescriptor(resolver)); - } - if (sort) { - Collections.sort(result, new Comparator<Object>() { - @Override public int compare(Object o1, Object o2) { - String s1 = o1 == null ? "" : o1.toString(); - String s2 = o2 == null ? "" : o2.toString(); - return s1.compareTo(s2); - } - }); - } - return result; - } - - public static Object toItemDescriptor(LocationResolver resolver) { - // TODO Get javadoc of LocationResolver? Could use docklet? But that would give dependency here - // on com.sun.javadoc.* - return resolver.getPrefix(); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/main/java/org/apache/brooklyn/cli/AbstractMain.java ---------------------------------------------------------------------- diff --git a/usage/cli/src/main/java/org/apache/brooklyn/cli/AbstractMain.java b/usage/cli/src/main/java/org/apache/brooklyn/cli/AbstractMain.java new file mode 100644 index 0000000..1d34abe --- /dev/null +++ b/usage/cli/src/main/java/org/apache/brooklyn/cli/AbstractMain.java @@ -0,0 +1,254 @@ +/* + * 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.command.Arguments; +import io.airlift.command.Cli; +import io.airlift.command.Cli.CliBuilder; +import io.airlift.command.Command; +import io.airlift.command.Help; +import io.airlift.command.Option; +import io.airlift.command.OptionType; +import io.airlift.command.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.concurrent.Callable; + +import javax.inject.Inject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.BrooklynVersion; +import org.apache.brooklyn.catalog.BrooklynCatalog; +import org.apache.brooklyn.cli.Main.LaunchCommand; + +import brooklyn.util.exceptions.FatalConfigurationRuntimeException; +import brooklyn.util.exceptions.FatalRuntimeException; +import brooklyn.util.exceptions.UserFacingException; +import brooklyn.util.text.Strings; + +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. + * <p> + * This class is designed for subclassing, with subclasses typically: + * <li> providing their own static {@link #main(String...)} (of course) which need simply invoke + * {@link #execCli(String[])} with the arguments + * <li> returning their CLI name (e.g. "start.sh") in an overridden {@link #cliScriptName()} + * <li> providing an overridden {@link LaunchCommand} via {@link #cliLaunchCommand()} if desired + * <li> providing any other CLI customisations by overriding {@link #cliBuilder()} + * (typically calling the parent and then customizing the builder) + * <li> 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<Void> { + + @Option(type = OptionType.GLOBAL, name = { "-v", "--verbose" }, description = "Verbose mode") + public boolean verbose = false; + + @Option(type = OptionType.GLOBAL, name = { "-q", "--quiet" }, description = "Quiet mode") + public boolean quiet = false; + + @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<String> arguments = new ArrayList<String>(); + + /** @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); + } + } + + @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.incubator.apache.org"); + System.out.println("Source: https://github.com/apache/incubator-brooklyn"); + System.out.println(); + System.out.println("Copyright 2011-2015 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<BrooklynCommand> cliBuilder(); + + protected void execCli(String ...args) { + execCli(cliBuilder().build(), args); + } + + protected void execCli(Cli<BrooklynCommand> 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<BrooklynCommand> parser) { + StringBuilder help = new StringBuilder(); + help.append("\n"); + Help.help(parser.getMetadata(), Collections.<String>emptyList(), help); + return help.toString(); + } + +}
