You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ha...@apache.org on 2015/08/05 22:56:06 UTC

[02/20] incubator-brooklyn git commit: Package rename to org.apache.brooklyn: usage-cli

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java
new file mode 100644
index 0000000..2ddad9d
--- /dev/null
+++ b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java
@@ -0,0 +1,153 @@
+/*
+ * 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.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/org/apache/brooklyn/cli/lister/ItemDescriptors.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ItemDescriptors.java b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ItemDescriptors.java
new file mode 100644
index 0000000..3912dd9
--- /dev/null
+++ b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ItemDescriptors.java
@@ -0,0 +1,173 @@
+/*
+ * 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.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/test/java/brooklyn/cli/CliTest.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/test/java/brooklyn/cli/CliTest.java b/usage/cli/src/test/java/brooklyn/cli/CliTest.java
deleted file mode 100644
index 9bf173c..0000000
--- a/usage/cli/src/test/java/brooklyn/cli/CliTest.java
+++ /dev/null
@@ -1,604 +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 org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-import groovy.lang.GroovyClassLoader;
-import io.airlift.command.Cli;
-import io.airlift.command.Command;
-import io.airlift.command.ParseException;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.regex.Pattern;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.cli.AbstractMain.BrooklynCommand;
-import brooklyn.cli.AbstractMain.BrooklynCommandCollectingArgs;
-import brooklyn.cli.AbstractMain.HelpCommand;
-import brooklyn.cli.Main.AppShutdownHandler;
-import brooklyn.cli.Main.GeneratePasswordCommand;
-import brooklyn.cli.Main.LaunchCommand;
-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.EntityProxy;
-import brooklyn.entity.proxying.ImplementedBy;
-import brooklyn.entity.trait.Startable;
-import brooklyn.location.Location;
-import brooklyn.location.basic.SimulatedLocation;
-import brooklyn.test.Asserts;
-import brooklyn.test.entity.LocalManagementContextForTests;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.exceptions.FatalConfigurationRuntimeException;
-import brooklyn.util.exceptions.UserFacingException;
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.io.Files;
-
-public class CliTest {
-
-    private static final Logger LOG = LoggerFactory.getLogger(CliTest.class);
-
-    // See testInvokeGroovyScript test for usage
-    public static final AtomicBoolean GROOVY_INVOKED = new AtomicBoolean(false);
-
-    private ExecutorService executor;
-    private StartableApplication app;
-    private static volatile ExampleEntity exampleEntity;
-
-    // static so that they can be set from the static classes ExampleApp and ExampleEntity
-    private static volatile boolean exampleAppRunning;
-    private static volatile boolean exampleAppConstructed;
-    private static volatile boolean exampleEntityRunning;
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        executor = Executors.newCachedThreadPool();
-        exampleAppConstructed = false;
-        exampleAppRunning = false;
-        exampleEntityRunning = false;
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (executor != null) executor.shutdownNow();
-        if (app != null) Entities.destroyAll(app.getManagementContext());
-        if (exampleEntity != null && exampleEntity.getApplication() != null) Entities.destroyAll(exampleEntity.getApplication().getManagementContext());
-    }
-    
-    @Test
-    public void testLoadApplicationFromClasspath() throws Exception {
-        String appName = ExampleApp.class.getName();
-        Object appBuilder = loadApplicationFromClasspathOrParse(appName);
-        assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder);
-        assertAppWrappedInBuilder((ApplicationBuilder)appBuilder, ExampleApp.class.getCanonicalName());
-    }
-
-    @Test
-    public void testLoadApplicationBuilderFromClasspath() throws Exception {
-        String appName = ExampleAppBuilder.class.getName();
-        Object appBuilder = loadApplicationFromClasspathOrParse(appName);
-        assertTrue(appBuilder instanceof ExampleAppBuilder, "app="+appBuilder);
-    }
-
-    @Test
-    public void testLoadEntityFromClasspath() throws Exception {
-        String entityName = ExampleEntity.class.getName();
-        Object appBuilder = loadApplicationFromClasspathOrParse(entityName);
-        assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder);
-        
-        app = ((ApplicationBuilder)appBuilder).manage();
-        Collection<Entity> entities = app.getChildren();
-        assertEquals(entities.size(), 1, "entities="+entities);
-        assertTrue(Iterables.getOnlyElement(entities) instanceof ExampleEntity, "entities="+entities+"; ifs="+Iterables.getOnlyElement(entities).getClass().getInterfaces());
-        assertTrue(Iterables.getOnlyElement(entities) instanceof EntityProxy, "entities="+entities);
-    }
-
-    @Deprecated // Tests deprecated approach of using impl directly
-    @Test
-    public void testLoadEntityImplFromClasspath() throws Exception {
-        String entityName = ExampleEntityImpl.class.getName();
-        Object appBuilder = loadApplicationFromClasspathOrParse(entityName);
-        assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder);
-        
-        app = ((ApplicationBuilder)appBuilder).manage();
-        Collection<Entity> entities = app.getChildren();
-        assertEquals(entities.size(), 1, "entities="+entities);
-        assertEquals(Iterables.getOnlyElement(entities).getEntityType().getName(), ExampleEntity.class.getCanonicalName(), "entities="+entities);
-        assertTrue(Iterables.getOnlyElement(entities) instanceof EntityProxy, "entities="+entities);
-    }
-
-    @Test
-    public void testLoadApplicationByParsingGroovyFile() throws Exception {
-        String appName = "ExampleAppInFile.groovy"; // file found in src/test/resources (contains empty app)
-        Object appBuilder = loadApplicationFromClasspathOrParse(appName);
-        assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder);
-        assertAppWrappedInBuilder((ApplicationBuilder)appBuilder, "ExampleAppInFile");
-    }
-    
-    private Object loadApplicationFromClasspathOrParse(String appName) throws Exception {
-        LaunchCommand launchCommand = new Main.LaunchCommand();
-        ResourceUtils resourceUtils = ResourceUtils.create(this);
-        GroovyClassLoader loader = new GroovyClassLoader(CliTest.class.getClassLoader());
-        return launchCommand.loadApplicationFromClasspathOrParse(resourceUtils, loader, appName);
-    }
-    
-    private void assertAppWrappedInBuilder(ApplicationBuilder builder, String expectedAppTypeName) {
-        StartableApplication app = builder.manage();
-        try {
-            String typeName = app.getEntityType().getName();
-            assertEquals(typeName, expectedAppTypeName, "app="+app+"; typeName="+typeName);
-        } finally {
-            Entities.destroyAll(app.getManagementContext());
-        }
-    }
-    
-    @Test
-    public void testInvokeGroovyScript() throws Exception {
-        File groovyFile = File.createTempFile("testinvokegroovy", "groovy");
-        try {
-            String contents = CliTest.class.getCanonicalName()+".GROOVY_INVOKED.set(true);";
-            Files.write(contents.getBytes(), groovyFile);
-
-            LaunchCommand launchCommand = new Main.LaunchCommand();
-            ResourceUtils resourceUtils = ResourceUtils.create(this);
-            GroovyClassLoader loader = new GroovyClassLoader(CliTest.class.getClassLoader());
-            launchCommand.execGroovyScript(resourceUtils, loader, groovyFile.toURI().toString());
-            assertTrue(GROOVY_INVOKED.get());
-            
-        } finally {
-            groovyFile.delete();
-            GROOVY_INVOKED.set(false);
-        }
-    }
-    
-    @Test
-    public void testStopAllApplications() throws Exception {
-        LaunchCommand launchCommand = new Main.LaunchCommand();
-        ExampleApp app = new ExampleApp();
-        try {
-            Entities.startManagement(app);
-            app.start(ImmutableList.of(new SimulatedLocation()));
-            assertTrue(app.running);
-            
-            launchCommand.stopAllApps(ImmutableList.of(app));
-            assertFalse(app.running);
-        } finally {
-            Entities.destroyAll(app.getManagementContext());
-        }
-    }
-    
-    @Test
-    public void testWaitsForInterrupt() throws Exception {
-        final AppShutdownHandler listener = new AppShutdownHandler();
-        Thread t = new Thread(new Runnable() {
-            @Override public void run() {
-                listener.waitOnShutdownRequest();
-            }});
-        
-        t.start();
-        t.join(100);
-        assertTrue(t.isAlive());
-        
-        t.interrupt();
-        t.join(10*1000);
-        assertFalse(t.isAlive());
-    }
-
-    protected Cli<BrooklynCommand> buildCli() {
-        return new Main().cliBuilder().build();
-    }
-    
-    @Test
-    public void testLaunchCommandParsesArgs() throws ParseException {
-        BrooklynCommand command = buildCli().parse("launch", 
-                "--app", "my.App", 
-                "--location", "localhost",
-                "--port", "1234",
-                "--bindAddress", "myhostname",
-                "--noConsole", "--noConsoleSecurity", "--stopOnKeyPress", 
-                "--localBrooklynProperties", "/path/to/myprops",
-                LaunchCommand.PERSIST_OPTION, LaunchCommand.PERSIST_OPTION_REBIND, 
-                "--persistenceDir", "/path/to/mypersist",
-                LaunchCommand.HA_OPTION, LaunchCommand.HA_OPTION_STANDBY);
-        assertTrue(command instanceof LaunchCommand, ""+command);
-        String details = command.toString();
-        assertTrue(details.contains("app=my.App"), details);   
-        assertTrue(details.contains("script=null"), details);
-        assertTrue(details.contains("location=localhost"), details);
-        assertTrue(details.contains("port=1234"), details);
-        assertTrue(details.contains("bindAddress=myhostname"), details);
-        assertTrue(details.contains("noConsole=true"), details);
-        assertTrue(details.contains("noConsoleSecurity=true"), details);
-        assertTrue(details.contains("stopOnKeyPress=true"), details);
-        assertTrue(details.contains("localBrooklynProperties=/path/to/myprops"), details);
-        assertTrue(details.contains("persist=rebind"), details);
-        assertTrue(details.contains("persistenceDir=/path/to/mypersist"), details);
-        assertTrue(details.contains("highAvailability=standby"), details);
-    }
-
-    @Test
-    public void testLaunchCommandUsesDefaults() throws ParseException {
-        BrooklynCommand command = buildCli().parse("launch");
-        assertTrue(command instanceof LaunchCommand, ""+command);
-        String details = command.toString();
-        assertTrue(details.contains("app=null"), details);   
-        assertTrue(details.contains("script=null"), details);
-        assertTrue(details.contains("location=null"), details);
-        assertTrue(details.contains("port=null"), details);
-        assertTrue(details.contains("noConsole=false"), details);
-        assertTrue(details.contains("noConsoleSecurity=false"), details);
-        assertTrue(details.contains("stopWhichAppsOnShutdown=theseIfNotPersisted"), details);
-        assertTrue(details.contains("stopOnKeyPress=false"), details);
-        assertTrue(details.contains("localBrooklynProperties=null"), details);
-        assertTrue(details.contains("persist=disabled"), details);
-        assertTrue(details.contains("persistenceDir=null"), details);
-        assertTrue(details.contains("highAvailability=auto"), details);
-    }
-
-    @Test
-    public void testLaunchCommandComplainsWithInvalidArgs() {
-        Cli<BrooklynCommand> cli = buildCli();
-        try {
-            BrooklynCommand command = cli.parse("launch", "invalid");
-            command.call();
-            Assert.fail("Should have thrown exception; instead got "+command);
-        } catch (ParseException e) {
-            /* expected */
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    @Test
-    public void testAppOptionIsOptional() throws ParseException {
-        Cli<BrooklynCommand> cli = buildCli();
-        cli.parse("launch", "blah", "my.App");
-    }
-    
-    public void testHelpCommand() {
-        Cli<BrooklynCommand> cli = buildCli();
-        BrooklynCommand command = cli.parse("help");
-        assertTrue(command instanceof HelpCommand);
-        command = cli.parse();
-        assertTrue(command instanceof HelpCommand);
-    }
-
-    @Test
-    public void testLaunchWillStartAppWhenGivenImpl() throws Exception {
-        Cli<BrooklynCommand> cli = buildCli();
-        BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", ExampleApp.class.getName(), "--location", "localhost");
-        submitCommandAndAssertRunnableSucceeds(command, new Runnable() {
-                public void run() {
-                    assertTrue(exampleAppConstructed);
-                    assertTrue(exampleAppRunning);
-                }
-            });
-    }
-
-    @Test
-    public void testLaunchStartsYamlApp() throws Exception {
-        Cli<BrooklynCommand> cli = buildCli();
-        BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-no-location.yaml", "--location", "localhost");
-        submitCommandAndAssertRunnableSucceeds(command, new Runnable() {
-                public void run() {
-                    assertTrue(exampleEntityRunning);
-                }
-            });
-    }
-    
-    @Test
-    public void testLaunchStartsYamlAppWithCommandLineLocation() throws Exception {
-        Cli<BrooklynCommand> cli = buildCli();
-        BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-no-location.yaml", "--location", "localhost:(name=testLocalhost)");
-        submitCommandAndAssertRunnableSucceeds(command, new Runnable() {
-                public void run() {
-                    assertTrue(exampleEntityRunning);
-                    assertTrue(Iterables.getOnlyElement(exampleEntity.getApplication().getLocations()).getDisplayName().equals("testLocalhost"));
-                }
-            });
-    }
-    
-    @Test
-    public void testLaunchStartsYamlAppWithYamlAppLocation() throws Exception {
-        Cli<BrooklynCommand> cli = buildCli();
-        BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-app-location.yaml");
-        submitCommandAndAssertRunnableSucceeds(command, new Runnable() {
-                public void run() {
-                    assertTrue(exampleEntityRunning);
-                    assertTrue(Iterables.getOnlyElement(exampleEntity.getApplication().getLocations()).getDisplayName().equals("appLocalhost"));
-                }
-            });
-    }
-    
-    @Test
-    public void testLaunchStartsYamlAppWithYamlAndAppCliLocation() throws Exception {
-        Cli<BrooklynCommand> cli = buildCli();
-        BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-app-location.yaml", "--location", "localhost");
-        submitCommandAndAssertRunnableSucceeds(command, new Runnable() {
-                public void run() {
-                    assertTrue(exampleEntityRunning);
-                    assertTrue(Iterables.getFirst(exampleEntity.getApplication().getLocations(), null).getDisplayName().equals("appLocalhost"));
-                }
-            });
-    }
-
-    @Test
-    public void testGeneratePasswordCommandParsed() throws Exception {
-        Cli<BrooklynCommand> cli = buildCli();
-        BrooklynCommand command = cli.parse("generate-password", "--user", "myname");
-        
-        assertTrue(command instanceof GeneratePasswordCommand);
-    }
-
-    @Test
-    public void testGeneratePasswordFromStdin() throws Exception {
-        List<String> stdoutLines = runCommand(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "mypassword\nmypassword\n");
-        
-        System.out.println(stdoutLines);
-    }
-
-    @Test
-    public void testGeneratePasswordFailsIfPasswordsDontMatch() throws Throwable {
-        Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "mypassword\ndifferentpassword\n");
-        if (exception instanceof UserFacingException && exception.toString().contains("Passwords did not match")) {
-            // success
-        } else {
-            throw new Exception(exception);
-        }
-    }
-
-    @Test
-    public void testGeneratePasswordFailsIfNoConsole() throws Throwable {
-        Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname"), "");
-        if (exception instanceof FatalConfigurationRuntimeException && exception.toString().contains("No console")) {
-            // success
-        } else {
-            throw new Exception(exception);
-        }
-    }
-    
-    @Test
-    public void testGeneratePasswordFailsIfPasswordBlank() throws Throwable {
-        Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "\n\n");
-        if (exception instanceof UserFacingException && exception.toString().contains("Password must not be blank")) {
-            // success
-        } else {
-            throw new Exception(exception);
-        }
-    }
-
-    @Test
-    public void testInfoShowsDefaultBanner() throws Exception {
-        List<String> stdoutLines = runCommand(ImmutableList.of("info"), "");
-        
-        for (String line : Splitter.on("\n").split(Main.DEFAULT_BANNER)) {
-            assertTrue(stdoutLines.contains(line), "out="+stdoutLines);
-        }
-    }
-
-    @Test
-    public void testInfoSupportsCustomizedBanner() throws Exception {
-        String origBanner = Main.banner;
-        String origBannerFirstLine = Iterables.get(Splitter.on("\n").split(Main.DEFAULT_BANNER), 0);
-        try {
-            String customBanner = "My Custom Banner";
-            Main.banner = customBanner;
-            List<String> stdoutLines = runCommand(ImmutableList.of("info"), "");
-            
-            assertTrue(stdoutLines.contains(customBanner), "out="+stdoutLines);
-            assertFalse(stdoutLines.contains(origBannerFirstLine), "out="+stdoutLines);
-        } finally {
-            Main.banner = origBanner;
-        }
-    }
-
-    @Test
-    public void testCanCustomiseInfoCommand() throws Exception {
-        Main main = new Main() {
-            protected Class<? extends BrooklynCommand> cliInfoCommand() {
-                return CustomInfoCommand.class;
-            }
-        };
-        List<String> stdoutLines = runCommand(main.cliBuilder().build(), ImmutableList.of("info"), "");
-        assertTrue(stdoutLines.contains("My Custom Info"), "out="+stdoutLines);
-    }
-    
-    @Command(name = "info", description = "Display information about brooklyn")
-    public static class CustomInfoCommand extends BrooklynCommandCollectingArgs {
-        @Override
-        public Void call() throws Exception {
-            System.out.println("My Custom Info");
-            return null;
-        }
-    }
-
-    @Test
-    public void testCanCustomiseLaunchCommand() throws Exception {
-        Main main = new Main() {
-            protected Class<? extends BrooklynCommand> cliLaunchCommand() {
-                return CustomLaunchCommand.class;
-            }
-        };
-        List<String> stdoutLines = runCommand(main.cliBuilder().build(), ImmutableList.of("launch"), "");
-        assertTrue(stdoutLines.contains("My Custom Launch"), "out="+stdoutLines);
-    }
-    
-    @Command(name = "launch", description = "Starts a server, optionally with applications")
-    public static class CustomLaunchCommand extends BrooklynCommandCollectingArgs {
-        @Override
-        public Void call() throws Exception {
-            System.out.println("My Custom Launch");
-            return null;
-        }
-    }
-
-    protected Throwable runCommandExpectingException(Iterable<String> args, String input) throws Exception {
-        try {
-            List<String> stdout = runCommand(args, input);
-            fail("Expected exception, but got stdout="+stdout);
-            return null;
-        } catch (ExecutionException e) {
-            return e.getCause();
-        }
-    }
-
-    protected List<String> runCommand(Iterable<String> args, String input) throws Exception {
-        Cli<BrooklynCommand> cli = buildCli();
-        return runCommand(cli, args, input);
-    }
-    
-    protected List<String> runCommand(Cli<BrooklynCommand> cli, Iterable<String> args, String input) throws Exception {
-        final BrooklynCommand command = cli.parse(args);
-        
-        final AtomicReference<Exception> exception = new AtomicReference<Exception>();
-        Thread t= new Thread(new Runnable() {
-            public void run() {
-                try {
-                    command.call();
-                } catch (Exception e) {
-                    exception.set(e);
-                    throw Exceptions.propagate(e);
-                }
-            }});
-        
-        InputStream origIn = System.in;
-        PrintStream origOut = System.out;
-        try {
-            InputStream stdin = new ByteArrayInputStream(input.getBytes());
-            System.setIn(stdin);
-
-            ByteArrayOutputStream stdoutBytes = new ByteArrayOutputStream();
-            PrintStream stdout = new PrintStream(stdoutBytes);
-            System.setOut(stdout);
-
-            t.start();
-
-            t.join(10*1000);
-            assertFalse(t.isAlive());
-            
-            if (exception.get() != null) {
-                throw new ExecutionException(exception.get());
-            }
-            
-            return ImmutableList.copyOf(Splitter.on(Pattern.compile("\r?\n")).split(new String(stdoutBytes.toByteArray())));
-        } finally {
-            System.setIn(origIn);
-            System.setOut(origOut);
-            t.interrupt();
-        }
-    }
-
-    private void submitCommandAndAssertRunnableSucceeds(final BrooklynCommand command, Runnable runnable) {
-        if (command instanceof LaunchCommand) {
-            ((LaunchCommand)command).useManagementContext(new LocalManagementContextForTests());
-        }
-        executor.submit(new Callable<Void>() {
-            public Void call() throws Exception {
-                try {
-                    LOG.info("Calling command: "+command);
-                    command.call();
-                    return null;
-                } catch (Throwable t) {
-                    LOG.error("Error executing command: "+t, t);
-                    throw Exceptions.propagate(t);
-                }
-            }});
-
-        Asserts.succeedsEventually(MutableMap.of("timeout", Duration.ONE_MINUTE), runnable);
-    }
-
-    //  An empty app to be used for testing
-    public static class ExampleApp extends AbstractApplication {
-        volatile boolean running;
-        volatile boolean constructed;
-        
-        @Override public void init() {
-            super.init();
-            constructed = true;
-            exampleAppConstructed = true;
-        }
-        @Override public void start(Collection<? extends Location> locations) {
-            super.start(locations);
-            running = true;
-            exampleAppRunning = true;
-        }
-        @Override public void stop() {
-            super.stop();
-            running = false;
-            exampleAppRunning = false;
-        }
-    }
-    
-    // An empty entity to be used for testing
-    @ImplementedBy(ExampleEntityImpl.class)
-    public static interface ExampleEntity extends Entity, Startable {
-    }   
-
-    public static class ExampleEntityImpl extends AbstractEntity implements ExampleEntity {
-        public ExampleEntityImpl() {
-            super();
-            exampleEntity = this;
-        }
-        @Override public void start(Collection<? extends Location> locations) {
-            exampleEntityRunning = true;
-        }
-        @Override public void stop() {
-            exampleEntityRunning = false;
-        }
-        @Override public void restart() {
-        }
-    }
-
-    // An empty app builder to be used for testing
-    public static class ExampleAppBuilder extends ApplicationBuilder {
-        @Override protected void doBuild() {
-            // no-op
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/java/brooklyn/cli/CloudExplorerLiveTest.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/test/java/brooklyn/cli/CloudExplorerLiveTest.java b/usage/cli/src/test/java/brooklyn/cli/CloudExplorerLiveTest.java
deleted file mode 100644
index 6069f74..0000000
--- a/usage/cli/src/test/java/brooklyn/cli/CloudExplorerLiveTest.java
+++ /dev/null
@@ -1,209 +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 org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-import io.airlift.command.Cli;
-import io.airlift.command.ParseException;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.util.List;
-
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.cli.AbstractMain.BrooklynCommand;
-
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-public class CloudExplorerLiveTest {
-
-    private String stdout;
-    private String stderr;
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() {
-        stdout = null;
-        stderr = null;
-    }
-
-    @Test
-    public void testNoArgsThrows() throws Exception {
-        try {
-            call(new String[0]);
-            Assert.fail("No args should fail");
-        } catch (ParseException e) {
-            Assert.assertTrue(e.toString().contains("No command specified"), ""+e);
-        }
-    }
-
-    // A user running these tests might not have any instances; so don't assert that there will be one
-    @Test(groups={"Live", "Live-sanity"})
-    public void testListInstances() throws Exception {
-        call("cloud-compute", "list-instances", "--location", "jclouds:aws-ec2:eu-west-1");
-        
-        String errmsg = "stdout="+stdout+"; stderr="+stderr;
-        
-        List<String> lines = assertAndStipSingleLocationHeader(stdout);
-        assertTrue(lines.get(0).equals("Instances {"), errmsg);
-        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
-        assertTrue(stderr.isEmpty(), errmsg);
-    }
-
-    @Test(groups={"Live", "Live-sanity"})
-    public void testListImages() throws Exception {
-        call("cloud-compute", "list-images", "--location", "jclouds:softlayer:ams01");
-        
-        String errmsg = "stdout="+stdout+"; stderr="+stderr;
-        
-        // FIXME Now has location details pre-amble; fix assertions
-        List<String> lines = assertAndStipSingleLocationHeader(stdout);
-        assertTrue(lines.get(0).equals("Images {"), errmsg);
-        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
-        assertTrue(stderr.isEmpty(), errmsg);
-        
-        List<String> imageLines = lines.subList(1, lines.size()-1);
-        assertTrue(imageLines.size() > 0, errmsg);
-        assertTrue(imageLines.get(0).matches(".*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg);
-    }
-
-    @Test(groups={"Live", "Live-sanity"})
-    public void testListHardwareProfiles() throws Exception {
-        call("cloud-compute", "list-hardware-profiles", "--location", "jclouds:softlayer:ams01");
-        
-        String errmsg = "stdout="+stdout+"; stderr="+stderr;
-        
-        // FIXME Now has location details pre-amble; fix assertions
-        List<String> lines = assertAndStipSingleLocationHeader(stdout);
-        assertTrue(lines.get(0).equals("Hardware Profiles {"), errmsg);
-        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
-        assertTrue(stderr.isEmpty(), errmsg);
-        
-        List<String> hardwareProfileLines = lines.subList(1, lines.size()-1);
-        assertTrue(hardwareProfileLines.size() > 0, errmsg);
-        assertTrue(hardwareProfileLines.get(0).matches(".*cpu=.*memory=.*processors=.*"), "line="+hardwareProfileLines.get(0)+"; "+errmsg);
-    }
-
-    @Test(groups={"Live", "Live-sanity"})
-    public void testGetImage() throws Exception {
-        call("cloud-compute", "get-image", "--location", "jclouds:softlayer:ams01", "CENTOS_6_64");
-        
-        String errmsg = "stdout="+stdout+"; stderr="+stderr;
-        
-        // FIXME Now has location details pre-amble; fix assertions
-        List<String> lines = assertAndStipSingleLocationHeader(stdout);
-        assertTrue(lines.get(0).equals("Image CENTOS_6_64 {"), errmsg);
-        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
-        assertTrue(stderr.isEmpty(), errmsg);
-        
-        List<String> imageLines = lines.subList(1, lines.size()-1);
-        assertTrue(imageLines.size() > 0, errmsg);
-        assertTrue(imageLines.get(0).matches(".*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg);
-    }
-
-    @Test(groups={"Live", "Live-sanity"})
-    public void testGetDefaultTemplate() throws Exception {
-        call("cloud-compute", "default-template", "--location", "jclouds:softlayer:ams01");
-        
-        String errmsg = "stdout="+stdout+"; stderr="+stderr;
-        
-        // FIXME Now has location details pre-amble; fix assertions
-        List<String> lines = assertAndStipSingleLocationHeader(stdout);
-        assertTrue(lines.get(0).equals("Default template {"), errmsg);
-        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
-        assertTrue(stderr.isEmpty(), errmsg);
-        
-        List<String> imageLines = lines.subList(1, lines.size()-1);
-        assertTrue(imageLines.size() > 0, errmsg);
-        assertTrue(imageLines.get(0).matches("\tImage.*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg);
-        assertTrue(imageLines.get(1).matches("\tHardware.*cpu=.*memory=.*processors=.*"), "line="+imageLines.get(1)+"; "+errmsg);
-        assertTrue(imageLines.get(2).matches("\tLocation.*scope=.*"), "line="+imageLines.get(2)+"; "+errmsg);
-        assertTrue(imageLines.get(3).matches("\tOptions.*"), "line="+imageLines.get(3)+"; "+errmsg);
-    }
-
-    /**
-     * Expects in brooklyn.properties:
-     *     brooklyn.location.named.softlayer-swift-ams01=jclouds:swift:https://ams01.objectstorage.softlayer.net/auth/v1.0
-     *     brooklyn.location.named.softlayer-swift-ams01.identity=ABCDEFGH:myusername
-     *     brooklyn.location.named.softlayer-swift-ams01.credential=1234567890...
-     */
-    @Test(groups={"Live", "Live-sanity"})
-    public void testListContainers() throws Exception {
-        call("cloud-blobstore", "list-containers", "--location", "named:softlayer-swift-ams01");
-        
-        String errmsg = "stdout="+stdout+"; stderr="+stderr;
-        
-        // FIXME Now has location details pre-amble; fix assertions
-        List<String> lines = assertAndStipSingleLocationHeader(stdout);
-        assertTrue(lines.get(0).equals("Containers {"), errmsg);
-        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
-        assertTrue(stderr.isEmpty(), errmsg);
-    }
-
-    protected void call(String... args) throws Exception {
-        call(new ByteArrayInputStream(new byte[0]), args);
-    }
-
-    protected void call(InputStream instream, String... args) throws Exception {
-        ByteArrayOutputStream stdoutStream = new ByteArrayOutputStream();
-        ByteArrayOutputStream stderrStream = new ByteArrayOutputStream();
-        
-        Cli<BrooklynCommand> parser = new Main().cliBuilder().build();
-        
-        BrooklynCommand command = parser.parse(args);
-        command.stdout = new PrintStream(stdoutStream);
-        command.stderr = new PrintStream(stderrStream);
-        command.stdin = instream;
-        try {
-            command.call();
-        } finally {
-            stdout = new String(stdoutStream.toByteArray());
-            stderr = new String(stderrStream.toByteArray());
-        }
-    }
-    
-    private List<String> assertAndStipSingleLocationHeader(String stdout) {
-        List<String> lines = ImmutableList.copyOf(Splitter.on("\n").omitEmptyStrings().split(stdout));
-
-        String errmsg = "lines="+lines;
-        
-        int nextLineCount = 0;
-        assertEquals(lines.get(nextLineCount++), "Location {", errmsg);
-        assertEquals(lines.get(lines.size()-1), "}", errmsg);
-        assertTrue(lines.get(nextLineCount++).startsWith("\tprovider: "), errmsg);
-        assertTrue(lines.get(nextLineCount++).startsWith("\tdisplayName: "), errmsg);
-        assertTrue(lines.get(nextLineCount++).startsWith("\tidentity: "), errmsg);
-        if (lines.get(nextLineCount).startsWith("\tendpoint: ")) nextLineCount++;
-        if (lines.get(nextLineCount).startsWith("\tregion: ")) nextLineCount++;
-        
-        List<String> result = Lists.newArrayList();
-        for (String line : lines.subList(nextLineCount, lines.size()-1)) {
-            assertTrue(line.startsWith("\t"), errmsg);
-            result.add(line.substring(1));
-        }
-        return result;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java b/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java
new file mode 100644
index 0000000..96e3e93
--- /dev/null
+++ b/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java
@@ -0,0 +1,605 @@
+/*
+ * 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 static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+import groovy.lang.GroovyClassLoader;
+import io.airlift.command.Cli;
+import io.airlift.command.Command;
+import io.airlift.command.ParseException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Pattern;
+
+import org.apache.brooklyn.cli.Main;
+import org.apache.brooklyn.cli.AbstractMain.BrooklynCommand;
+import org.apache.brooklyn.cli.AbstractMain.BrooklynCommandCollectingArgs;
+import org.apache.brooklyn.cli.AbstractMain.HelpCommand;
+import org.apache.brooklyn.cli.Main.AppShutdownHandler;
+import org.apache.brooklyn.cli.Main.GeneratePasswordCommand;
+import org.apache.brooklyn.cli.Main.LaunchCommand;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+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.EntityProxy;
+import brooklyn.entity.proxying.ImplementedBy;
+import brooklyn.entity.trait.Startable;
+import brooklyn.location.Location;
+import brooklyn.location.basic.SimulatedLocation;
+import brooklyn.test.Asserts;
+import brooklyn.test.entity.LocalManagementContextForTests;
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.FatalConfigurationRuntimeException;
+import brooklyn.util.exceptions.UserFacingException;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Files;
+
+public class CliTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CliTest.class);
+
+    // See testInvokeGroovyScript test for usage
+    public static final AtomicBoolean GROOVY_INVOKED = new AtomicBoolean(false);
+
+    private ExecutorService executor;
+    private StartableApplication app;
+    private static volatile ExampleEntity exampleEntity;
+
+    // static so that they can be set from the static classes ExampleApp and ExampleEntity
+    private static volatile boolean exampleAppRunning;
+    private static volatile boolean exampleAppConstructed;
+    private static volatile boolean exampleEntityRunning;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        executor = Executors.newCachedThreadPool();
+        exampleAppConstructed = false;
+        exampleAppRunning = false;
+        exampleEntityRunning = false;
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (executor != null) executor.shutdownNow();
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+        if (exampleEntity != null && exampleEntity.getApplication() != null) Entities.destroyAll(exampleEntity.getApplication().getManagementContext());
+    }
+    
+    @Test
+    public void testLoadApplicationFromClasspath() throws Exception {
+        String appName = ExampleApp.class.getName();
+        Object appBuilder = loadApplicationFromClasspathOrParse(appName);
+        assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder);
+        assertAppWrappedInBuilder((ApplicationBuilder)appBuilder, ExampleApp.class.getCanonicalName());
+    }
+
+    @Test
+    public void testLoadApplicationBuilderFromClasspath() throws Exception {
+        String appName = ExampleAppBuilder.class.getName();
+        Object appBuilder = loadApplicationFromClasspathOrParse(appName);
+        assertTrue(appBuilder instanceof ExampleAppBuilder, "app="+appBuilder);
+    }
+
+    @Test
+    public void testLoadEntityFromClasspath() throws Exception {
+        String entityName = ExampleEntity.class.getName();
+        Object appBuilder = loadApplicationFromClasspathOrParse(entityName);
+        assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder);
+        
+        app = ((ApplicationBuilder)appBuilder).manage();
+        Collection<Entity> entities = app.getChildren();
+        assertEquals(entities.size(), 1, "entities="+entities);
+        assertTrue(Iterables.getOnlyElement(entities) instanceof ExampleEntity, "entities="+entities+"; ifs="+Iterables.getOnlyElement(entities).getClass().getInterfaces());
+        assertTrue(Iterables.getOnlyElement(entities) instanceof EntityProxy, "entities="+entities);
+    }
+
+    @Deprecated // Tests deprecated approach of using impl directly
+    @Test
+    public void testLoadEntityImplFromClasspath() throws Exception {
+        String entityName = ExampleEntityImpl.class.getName();
+        Object appBuilder = loadApplicationFromClasspathOrParse(entityName);
+        assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder);
+        
+        app = ((ApplicationBuilder)appBuilder).manage();
+        Collection<Entity> entities = app.getChildren();
+        assertEquals(entities.size(), 1, "entities="+entities);
+        assertEquals(Iterables.getOnlyElement(entities).getEntityType().getName(), ExampleEntity.class.getCanonicalName(), "entities="+entities);
+        assertTrue(Iterables.getOnlyElement(entities) instanceof EntityProxy, "entities="+entities);
+    }
+
+    @Test
+    public void testLoadApplicationByParsingGroovyFile() throws Exception {
+        String appName = "ExampleAppInFile.groovy"; // file found in src/test/resources (contains empty app)
+        Object appBuilder = loadApplicationFromClasspathOrParse(appName);
+        assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder);
+        assertAppWrappedInBuilder((ApplicationBuilder)appBuilder, "ExampleAppInFile");
+    }
+    
+    private Object loadApplicationFromClasspathOrParse(String appName) throws Exception {
+        LaunchCommand launchCommand = new Main.LaunchCommand();
+        ResourceUtils resourceUtils = ResourceUtils.create(this);
+        GroovyClassLoader loader = new GroovyClassLoader(CliTest.class.getClassLoader());
+        return launchCommand.loadApplicationFromClasspathOrParse(resourceUtils, loader, appName);
+    }
+    
+    private void assertAppWrappedInBuilder(ApplicationBuilder builder, String expectedAppTypeName) {
+        StartableApplication app = builder.manage();
+        try {
+            String typeName = app.getEntityType().getName();
+            assertEquals(typeName, expectedAppTypeName, "app="+app+"; typeName="+typeName);
+        } finally {
+            Entities.destroyAll(app.getManagementContext());
+        }
+    }
+    
+    @Test
+    public void testInvokeGroovyScript() throws Exception {
+        File groovyFile = File.createTempFile("testinvokegroovy", "groovy");
+        try {
+            String contents = CliTest.class.getCanonicalName()+".GROOVY_INVOKED.set(true);";
+            Files.write(contents.getBytes(), groovyFile);
+
+            LaunchCommand launchCommand = new Main.LaunchCommand();
+            ResourceUtils resourceUtils = ResourceUtils.create(this);
+            GroovyClassLoader loader = new GroovyClassLoader(CliTest.class.getClassLoader());
+            launchCommand.execGroovyScript(resourceUtils, loader, groovyFile.toURI().toString());
+            assertTrue(GROOVY_INVOKED.get());
+            
+        } finally {
+            groovyFile.delete();
+            GROOVY_INVOKED.set(false);
+        }
+    }
+    
+    @Test
+    public void testStopAllApplications() throws Exception {
+        LaunchCommand launchCommand = new Main.LaunchCommand();
+        ExampleApp app = new ExampleApp();
+        try {
+            Entities.startManagement(app);
+            app.start(ImmutableList.of(new SimulatedLocation()));
+            assertTrue(app.running);
+            
+            launchCommand.stopAllApps(ImmutableList.of(app));
+            assertFalse(app.running);
+        } finally {
+            Entities.destroyAll(app.getManagementContext());
+        }
+    }
+    
+    @Test
+    public void testWaitsForInterrupt() throws Exception {
+        final AppShutdownHandler listener = new AppShutdownHandler();
+        Thread t = new Thread(new Runnable() {
+            @Override public void run() {
+                listener.waitOnShutdownRequest();
+            }});
+        
+        t.start();
+        t.join(100);
+        assertTrue(t.isAlive());
+        
+        t.interrupt();
+        t.join(10*1000);
+        assertFalse(t.isAlive());
+    }
+
+    protected Cli<BrooklynCommand> buildCli() {
+        return new Main().cliBuilder().build();
+    }
+    
+    @Test
+    public void testLaunchCommandParsesArgs() throws ParseException {
+        BrooklynCommand command = buildCli().parse("launch", 
+                "--app", "my.App", 
+                "--location", "localhost",
+                "--port", "1234",
+                "--bindAddress", "myhostname",
+                "--noConsole", "--noConsoleSecurity", "--stopOnKeyPress", 
+                "--localBrooklynProperties", "/path/to/myprops",
+                LaunchCommand.PERSIST_OPTION, LaunchCommand.PERSIST_OPTION_REBIND, 
+                "--persistenceDir", "/path/to/mypersist",
+                LaunchCommand.HA_OPTION, LaunchCommand.HA_OPTION_STANDBY);
+        assertTrue(command instanceof LaunchCommand, ""+command);
+        String details = command.toString();
+        assertTrue(details.contains("app=my.App"), details);   
+        assertTrue(details.contains("script=null"), details);
+        assertTrue(details.contains("location=localhost"), details);
+        assertTrue(details.contains("port=1234"), details);
+        assertTrue(details.contains("bindAddress=myhostname"), details);
+        assertTrue(details.contains("noConsole=true"), details);
+        assertTrue(details.contains("noConsoleSecurity=true"), details);
+        assertTrue(details.contains("stopOnKeyPress=true"), details);
+        assertTrue(details.contains("localBrooklynProperties=/path/to/myprops"), details);
+        assertTrue(details.contains("persist=rebind"), details);
+        assertTrue(details.contains("persistenceDir=/path/to/mypersist"), details);
+        assertTrue(details.contains("highAvailability=standby"), details);
+    }
+
+    @Test
+    public void testLaunchCommandUsesDefaults() throws ParseException {
+        BrooklynCommand command = buildCli().parse("launch");
+        assertTrue(command instanceof LaunchCommand, ""+command);
+        String details = command.toString();
+        assertTrue(details.contains("app=null"), details);   
+        assertTrue(details.contains("script=null"), details);
+        assertTrue(details.contains("location=null"), details);
+        assertTrue(details.contains("port=null"), details);
+        assertTrue(details.contains("noConsole=false"), details);
+        assertTrue(details.contains("noConsoleSecurity=false"), details);
+        assertTrue(details.contains("stopWhichAppsOnShutdown=theseIfNotPersisted"), details);
+        assertTrue(details.contains("stopOnKeyPress=false"), details);
+        assertTrue(details.contains("localBrooklynProperties=null"), details);
+        assertTrue(details.contains("persist=disabled"), details);
+        assertTrue(details.contains("persistenceDir=null"), details);
+        assertTrue(details.contains("highAvailability=auto"), details);
+    }
+
+    @Test
+    public void testLaunchCommandComplainsWithInvalidArgs() {
+        Cli<BrooklynCommand> cli = buildCli();
+        try {
+            BrooklynCommand command = cli.parse("launch", "invalid");
+            command.call();
+            Assert.fail("Should have thrown exception; instead got "+command);
+        } catch (ParseException e) {
+            /* expected */
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    @Test
+    public void testAppOptionIsOptional() throws ParseException {
+        Cli<BrooklynCommand> cli = buildCli();
+        cli.parse("launch", "blah", "my.App");
+    }
+    
+    public void testHelpCommand() {
+        Cli<BrooklynCommand> cli = buildCli();
+        BrooklynCommand command = cli.parse("help");
+        assertTrue(command instanceof HelpCommand);
+        command = cli.parse();
+        assertTrue(command instanceof HelpCommand);
+    }
+
+    @Test
+    public void testLaunchWillStartAppWhenGivenImpl() throws Exception {
+        Cli<BrooklynCommand> cli = buildCli();
+        BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", ExampleApp.class.getName(), "--location", "localhost");
+        submitCommandAndAssertRunnableSucceeds(command, new Runnable() {
+                public void run() {
+                    assertTrue(exampleAppConstructed);
+                    assertTrue(exampleAppRunning);
+                }
+            });
+    }
+
+    @Test
+    public void testLaunchStartsYamlApp() throws Exception {
+        Cli<BrooklynCommand> cli = buildCli();
+        BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-no-location.yaml", "--location", "localhost");
+        submitCommandAndAssertRunnableSucceeds(command, new Runnable() {
+                public void run() {
+                    assertTrue(exampleEntityRunning);
+                }
+            });
+    }
+    
+    @Test
+    public void testLaunchStartsYamlAppWithCommandLineLocation() throws Exception {
+        Cli<BrooklynCommand> cli = buildCli();
+        BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-no-location.yaml", "--location", "localhost:(name=testLocalhost)");
+        submitCommandAndAssertRunnableSucceeds(command, new Runnable() {
+                public void run() {
+                    assertTrue(exampleEntityRunning);
+                    assertTrue(Iterables.getOnlyElement(exampleEntity.getApplication().getLocations()).getDisplayName().equals("testLocalhost"));
+                }
+            });
+    }
+    
+    @Test
+    public void testLaunchStartsYamlAppWithYamlAppLocation() throws Exception {
+        Cli<BrooklynCommand> cli = buildCli();
+        BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-app-location.yaml");
+        submitCommandAndAssertRunnableSucceeds(command, new Runnable() {
+                public void run() {
+                    assertTrue(exampleEntityRunning);
+                    assertTrue(Iterables.getOnlyElement(exampleEntity.getApplication().getLocations()).getDisplayName().equals("appLocalhost"));
+                }
+            });
+    }
+    
+    @Test
+    public void testLaunchStartsYamlAppWithYamlAndAppCliLocation() throws Exception {
+        Cli<BrooklynCommand> cli = buildCli();
+        BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-app-location.yaml", "--location", "localhost");
+        submitCommandAndAssertRunnableSucceeds(command, new Runnable() {
+                public void run() {
+                    assertTrue(exampleEntityRunning);
+                    assertTrue(Iterables.getFirst(exampleEntity.getApplication().getLocations(), null).getDisplayName().equals("appLocalhost"));
+                }
+            });
+    }
+
+    @Test
+    public void testGeneratePasswordCommandParsed() throws Exception {
+        Cli<BrooklynCommand> cli = buildCli();
+        BrooklynCommand command = cli.parse("generate-password", "--user", "myname");
+        
+        assertTrue(command instanceof GeneratePasswordCommand);
+    }
+
+    @Test
+    public void testGeneratePasswordFromStdin() throws Exception {
+        List<String> stdoutLines = runCommand(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "mypassword\nmypassword\n");
+        
+        System.out.println(stdoutLines);
+    }
+
+    @Test
+    public void testGeneratePasswordFailsIfPasswordsDontMatch() throws Throwable {
+        Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "mypassword\ndifferentpassword\n");
+        if (exception instanceof UserFacingException && exception.toString().contains("Passwords did not match")) {
+            // success
+        } else {
+            throw new Exception(exception);
+        }
+    }
+
+    @Test
+    public void testGeneratePasswordFailsIfNoConsole() throws Throwable {
+        Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname"), "");
+        if (exception instanceof FatalConfigurationRuntimeException && exception.toString().contains("No console")) {
+            // success
+        } else {
+            throw new Exception(exception);
+        }
+    }
+    
+    @Test
+    public void testGeneratePasswordFailsIfPasswordBlank() throws Throwable {
+        Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "\n\n");
+        if (exception instanceof UserFacingException && exception.toString().contains("Password must not be blank")) {
+            // success
+        } else {
+            throw new Exception(exception);
+        }
+    }
+
+    @Test
+    public void testInfoShowsDefaultBanner() throws Exception {
+        List<String> stdoutLines = runCommand(ImmutableList.of("info"), "");
+        
+        for (String line : Splitter.on("\n").split(Main.DEFAULT_BANNER)) {
+            assertTrue(stdoutLines.contains(line), "out="+stdoutLines);
+        }
+    }
+
+    @Test
+    public void testInfoSupportsCustomizedBanner() throws Exception {
+        String origBanner = Main.banner;
+        String origBannerFirstLine = Iterables.get(Splitter.on("\n").split(Main.DEFAULT_BANNER), 0);
+        try {
+            String customBanner = "My Custom Banner";
+            Main.banner = customBanner;
+            List<String> stdoutLines = runCommand(ImmutableList.of("info"), "");
+            
+            assertTrue(stdoutLines.contains(customBanner), "out="+stdoutLines);
+            assertFalse(stdoutLines.contains(origBannerFirstLine), "out="+stdoutLines);
+        } finally {
+            Main.banner = origBanner;
+        }
+    }
+
+    @Test
+    public void testCanCustomiseInfoCommand() throws Exception {
+        Main main = new Main() {
+            protected Class<? extends BrooklynCommand> cliInfoCommand() {
+                return CustomInfoCommand.class;
+            }
+        };
+        List<String> stdoutLines = runCommand(main.cliBuilder().build(), ImmutableList.of("info"), "");
+        assertTrue(stdoutLines.contains("My Custom Info"), "out="+stdoutLines);
+    }
+    
+    @Command(name = "info", description = "Display information about brooklyn")
+    public static class CustomInfoCommand extends BrooklynCommandCollectingArgs {
+        @Override
+        public Void call() throws Exception {
+            System.out.println("My Custom Info");
+            return null;
+        }
+    }
+
+    @Test
+    public void testCanCustomiseLaunchCommand() throws Exception {
+        Main main = new Main() {
+            protected Class<? extends BrooklynCommand> cliLaunchCommand() {
+                return CustomLaunchCommand.class;
+            }
+        };
+        List<String> stdoutLines = runCommand(main.cliBuilder().build(), ImmutableList.of("launch"), "");
+        assertTrue(stdoutLines.contains("My Custom Launch"), "out="+stdoutLines);
+    }
+    
+    @Command(name = "launch", description = "Starts a server, optionally with applications")
+    public static class CustomLaunchCommand extends BrooklynCommandCollectingArgs {
+        @Override
+        public Void call() throws Exception {
+            System.out.println("My Custom Launch");
+            return null;
+        }
+    }
+
+    protected Throwable runCommandExpectingException(Iterable<String> args, String input) throws Exception {
+        try {
+            List<String> stdout = runCommand(args, input);
+            fail("Expected exception, but got stdout="+stdout);
+            return null;
+        } catch (ExecutionException e) {
+            return e.getCause();
+        }
+    }
+
+    protected List<String> runCommand(Iterable<String> args, String input) throws Exception {
+        Cli<BrooklynCommand> cli = buildCli();
+        return runCommand(cli, args, input);
+    }
+    
+    protected List<String> runCommand(Cli<BrooklynCommand> cli, Iterable<String> args, String input) throws Exception {
+        final BrooklynCommand command = cli.parse(args);
+        
+        final AtomicReference<Exception> exception = new AtomicReference<Exception>();
+        Thread t= new Thread(new Runnable() {
+            public void run() {
+                try {
+                    command.call();
+                } catch (Exception e) {
+                    exception.set(e);
+                    throw Exceptions.propagate(e);
+                }
+            }});
+        
+        InputStream origIn = System.in;
+        PrintStream origOut = System.out;
+        try {
+            InputStream stdin = new ByteArrayInputStream(input.getBytes());
+            System.setIn(stdin);
+
+            ByteArrayOutputStream stdoutBytes = new ByteArrayOutputStream();
+            PrintStream stdout = new PrintStream(stdoutBytes);
+            System.setOut(stdout);
+
+            t.start();
+
+            t.join(10*1000);
+            assertFalse(t.isAlive());
+            
+            if (exception.get() != null) {
+                throw new ExecutionException(exception.get());
+            }
+            
+            return ImmutableList.copyOf(Splitter.on(Pattern.compile("\r?\n")).split(new String(stdoutBytes.toByteArray())));
+        } finally {
+            System.setIn(origIn);
+            System.setOut(origOut);
+            t.interrupt();
+        }
+    }
+
+    private void submitCommandAndAssertRunnableSucceeds(final BrooklynCommand command, Runnable runnable) {
+        if (command instanceof LaunchCommand) {
+            ((LaunchCommand)command).useManagementContext(new LocalManagementContextForTests());
+        }
+        executor.submit(new Callable<Void>() {
+            public Void call() throws Exception {
+                try {
+                    LOG.info("Calling command: "+command);
+                    command.call();
+                    return null;
+                } catch (Throwable t) {
+                    LOG.error("Error executing command: "+t, t);
+                    throw Exceptions.propagate(t);
+                }
+            }});
+
+        Asserts.succeedsEventually(MutableMap.of("timeout", Duration.ONE_MINUTE), runnable);
+    }
+
+    //  An empty app to be used for testing
+    public static class ExampleApp extends AbstractApplication {
+        volatile boolean running;
+        volatile boolean constructed;
+        
+        @Override public void init() {
+            super.init();
+            constructed = true;
+            exampleAppConstructed = true;
+        }
+        @Override public void start(Collection<? extends Location> locations) {
+            super.start(locations);
+            running = true;
+            exampleAppRunning = true;
+        }
+        @Override public void stop() {
+            super.stop();
+            running = false;
+            exampleAppRunning = false;
+        }
+    }
+    
+    // An empty entity to be used for testing
+    @ImplementedBy(ExampleEntityImpl.class)
+    public static interface ExampleEntity extends Entity, Startable {
+    }   
+
+    public static class ExampleEntityImpl extends AbstractEntity implements ExampleEntity {
+        public ExampleEntityImpl() {
+            super();
+            exampleEntity = this;
+        }
+        @Override public void start(Collection<? extends Location> locations) {
+            exampleEntityRunning = true;
+        }
+        @Override public void stop() {
+            exampleEntityRunning = false;
+        }
+        @Override public void restart() {
+        }
+    }
+
+    // An empty app builder to be used for testing
+    public static class ExampleAppBuilder extends ApplicationBuilder {
+        @Override protected void doBuild() {
+            // no-op
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/java/org/apache/brooklyn/cli/CloudExplorerLiveTest.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/test/java/org/apache/brooklyn/cli/CloudExplorerLiveTest.java b/usage/cli/src/test/java/org/apache/brooklyn/cli/CloudExplorerLiveTest.java
new file mode 100644
index 0000000..8abe1c0
--- /dev/null
+++ b/usage/cli/src/test/java/org/apache/brooklyn/cli/CloudExplorerLiveTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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 static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import io.airlift.command.Cli;
+import io.airlift.command.ParseException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.List;
+
+import org.apache.brooklyn.cli.Main;
+import org.apache.brooklyn.cli.AbstractMain.BrooklynCommand;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+public class CloudExplorerLiveTest {
+
+    private String stdout;
+    private String stderr;
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() {
+        stdout = null;
+        stderr = null;
+    }
+
+    @Test
+    public void testNoArgsThrows() throws Exception {
+        try {
+            call(new String[0]);
+            Assert.fail("No args should fail");
+        } catch (ParseException e) {
+            Assert.assertTrue(e.toString().contains("No command specified"), ""+e);
+        }
+    }
+
+    // A user running these tests might not have any instances; so don't assert that there will be one
+    @Test(groups={"Live", "Live-sanity"})
+    public void testListInstances() throws Exception {
+        call("cloud-compute", "list-instances", "--location", "jclouds:aws-ec2:eu-west-1");
+        
+        String errmsg = "stdout="+stdout+"; stderr="+stderr;
+        
+        List<String> lines = assertAndStipSingleLocationHeader(stdout);
+        assertTrue(lines.get(0).equals("Instances {"), errmsg);
+        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
+        assertTrue(stderr.isEmpty(), errmsg);
+    }
+
+    @Test(groups={"Live", "Live-sanity"})
+    public void testListImages() throws Exception {
+        call("cloud-compute", "list-images", "--location", "jclouds:softlayer:ams01");
+        
+        String errmsg = "stdout="+stdout+"; stderr="+stderr;
+        
+        // FIXME Now has location details pre-amble; fix assertions
+        List<String> lines = assertAndStipSingleLocationHeader(stdout);
+        assertTrue(lines.get(0).equals("Images {"), errmsg);
+        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
+        assertTrue(stderr.isEmpty(), errmsg);
+        
+        List<String> imageLines = lines.subList(1, lines.size()-1);
+        assertTrue(imageLines.size() > 0, errmsg);
+        assertTrue(imageLines.get(0).matches(".*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg);
+    }
+
+    @Test(groups={"Live", "Live-sanity"})
+    public void testListHardwareProfiles() throws Exception {
+        call("cloud-compute", "list-hardware-profiles", "--location", "jclouds:softlayer:ams01");
+        
+        String errmsg = "stdout="+stdout+"; stderr="+stderr;
+        
+        // FIXME Now has location details pre-amble; fix assertions
+        List<String> lines = assertAndStipSingleLocationHeader(stdout);
+        assertTrue(lines.get(0).equals("Hardware Profiles {"), errmsg);
+        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
+        assertTrue(stderr.isEmpty(), errmsg);
+        
+        List<String> hardwareProfileLines = lines.subList(1, lines.size()-1);
+        assertTrue(hardwareProfileLines.size() > 0, errmsg);
+        assertTrue(hardwareProfileLines.get(0).matches(".*cpu=.*memory=.*processors=.*"), "line="+hardwareProfileLines.get(0)+"; "+errmsg);
+    }
+
+    @Test(groups={"Live", "Live-sanity"})
+    public void testGetImage() throws Exception {
+        call("cloud-compute", "get-image", "--location", "jclouds:softlayer:ams01", "CENTOS_6_64");
+        
+        String errmsg = "stdout="+stdout+"; stderr="+stderr;
+        
+        // FIXME Now has location details pre-amble; fix assertions
+        List<String> lines = assertAndStipSingleLocationHeader(stdout);
+        assertTrue(lines.get(0).equals("Image CENTOS_6_64 {"), errmsg);
+        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
+        assertTrue(stderr.isEmpty(), errmsg);
+        
+        List<String> imageLines = lines.subList(1, lines.size()-1);
+        assertTrue(imageLines.size() > 0, errmsg);
+        assertTrue(imageLines.get(0).matches(".*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg);
+    }
+
+    @Test(groups={"Live", "Live-sanity"})
+    public void testGetDefaultTemplate() throws Exception {
+        call("cloud-compute", "default-template", "--location", "jclouds:softlayer:ams01");
+        
+        String errmsg = "stdout="+stdout+"; stderr="+stderr;
+        
+        // FIXME Now has location details pre-amble; fix assertions
+        List<String> lines = assertAndStipSingleLocationHeader(stdout);
+        assertTrue(lines.get(0).equals("Default template {"), errmsg);
+        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
+        assertTrue(stderr.isEmpty(), errmsg);
+        
+        List<String> imageLines = lines.subList(1, lines.size()-1);
+        assertTrue(imageLines.size() > 0, errmsg);
+        assertTrue(imageLines.get(0).matches("\tImage.*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg);
+        assertTrue(imageLines.get(1).matches("\tHardware.*cpu=.*memory=.*processors=.*"), "line="+imageLines.get(1)+"; "+errmsg);
+        assertTrue(imageLines.get(2).matches("\tLocation.*scope=.*"), "line="+imageLines.get(2)+"; "+errmsg);
+        assertTrue(imageLines.get(3).matches("\tOptions.*"), "line="+imageLines.get(3)+"; "+errmsg);
+    }
+
+    /**
+     * Expects in brooklyn.properties:
+     *     brooklyn.location.named.softlayer-swift-ams01=jclouds:swift:https://ams01.objectstorage.softlayer.net/auth/v1.0
+     *     brooklyn.location.named.softlayer-swift-ams01.identity=ABCDEFGH:myusername
+     *     brooklyn.location.named.softlayer-swift-ams01.credential=1234567890...
+     */
+    @Test(groups={"Live", "Live-sanity"})
+    public void testListContainers() throws Exception {
+        call("cloud-blobstore", "list-containers", "--location", "named:softlayer-swift-ams01");
+        
+        String errmsg = "stdout="+stdout+"; stderr="+stderr;
+        
+        // FIXME Now has location details pre-amble; fix assertions
+        List<String> lines = assertAndStipSingleLocationHeader(stdout);
+        assertTrue(lines.get(0).equals("Containers {"), errmsg);
+        assertTrue(lines.get(lines.size()-1).equals("}"), errmsg);
+        assertTrue(stderr.isEmpty(), errmsg);
+    }
+
+    protected void call(String... args) throws Exception {
+        call(new ByteArrayInputStream(new byte[0]), args);
+    }
+
+    protected void call(InputStream instream, String... args) throws Exception {
+        ByteArrayOutputStream stdoutStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream stderrStream = new ByteArrayOutputStream();
+        
+        Cli<BrooklynCommand> parser = new Main().cliBuilder().build();
+        
+        BrooklynCommand command = parser.parse(args);
+        command.stdout = new PrintStream(stdoutStream);
+        command.stderr = new PrintStream(stderrStream);
+        command.stdin = instream;
+        try {
+            command.call();
+        } finally {
+            stdout = new String(stdoutStream.toByteArray());
+            stderr = new String(stderrStream.toByteArray());
+        }
+    }
+    
+    private List<String> assertAndStipSingleLocationHeader(String stdout) {
+        List<String> lines = ImmutableList.copyOf(Splitter.on("\n").omitEmptyStrings().split(stdout));
+
+        String errmsg = "lines="+lines;
+        
+        int nextLineCount = 0;
+        assertEquals(lines.get(nextLineCount++), "Location {", errmsg);
+        assertEquals(lines.get(lines.size()-1), "}", errmsg);
+        assertTrue(lines.get(nextLineCount++).startsWith("\tprovider: "), errmsg);
+        assertTrue(lines.get(nextLineCount++).startsWith("\tdisplayName: "), errmsg);
+        assertTrue(lines.get(nextLineCount++).startsWith("\tidentity: "), errmsg);
+        if (lines.get(nextLineCount).startsWith("\tendpoint: ")) nextLineCount++;
+        if (lines.get(nextLineCount).startsWith("\tregion: ")) nextLineCount++;
+        
+        List<String> result = Lists.newArrayList();
+        for (String line : lines.subList(nextLineCount, lines.size()-1)) {
+            assertTrue(line.startsWith("\t"), errmsg);
+            result.add(line.substring(1));
+        }
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/resources/example-app-app-location.yaml
----------------------------------------------------------------------
diff --git a/usage/cli/src/test/resources/example-app-app-location.yaml b/usage/cli/src/test/resources/example-app-app-location.yaml
index aca5362..f161e8d 100644
--- a/usage/cli/src/test/resources/example-app-app-location.yaml
+++ b/usage/cli/src/test/resources/example-app-app-location.yaml
@@ -19,5 +19,5 @@
 name: example-app-app-location
 location: localhost:(name=appLocalhost)
 services:
-- serviceType: brooklyn.cli.CliTest$ExampleEntity
+- serviceType: org.apache.brooklyn.cli.CliTest$ExampleEntity
   name: Example app
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/resources/example-app-entity-location.yaml
----------------------------------------------------------------------
diff --git a/usage/cli/src/test/resources/example-app-entity-location.yaml b/usage/cli/src/test/resources/example-app-entity-location.yaml
index cbca856..5b2e466 100644
--- a/usage/cli/src/test/resources/example-app-entity-location.yaml
+++ b/usage/cli/src/test/resources/example-app-entity-location.yaml
@@ -18,6 +18,6 @@
 #
 name: example-app-entity-location
 services:
-- serviceType: brooklyn.cli.CliTest$ExampleEntity
+- serviceType: org.apache.brooklyn.cli.CliTest$ExampleEntity
   name: Example app
   location: localhost:(name=entityLocalhost)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/resources/example-app-no-location.yaml
----------------------------------------------------------------------
diff --git a/usage/cli/src/test/resources/example-app-no-location.yaml b/usage/cli/src/test/resources/example-app-no-location.yaml
index 9592e61..60cff1c 100644
--- a/usage/cli/src/test/resources/example-app-no-location.yaml
+++ b/usage/cli/src/test/resources/example-app-no-location.yaml
@@ -18,5 +18,5 @@
 #
 name: example-app-no-location
 services:
-- serviceType: brooklyn.cli.CliTest$ExampleEntity
+- serviceType: org.apache.brooklyn.cli.CliTest$ExampleEntity
   name: Example app