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/07 03:09:46 UTC

[2/8] incubator-brooklyn git commit: brooklyn-launcher: add org.apache package prefix

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6f58ef3e/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
new file mode 100644
index 0000000..66a9f67
--- /dev/null
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
@@ -0,0 +1,258 @@
+/*
+ * 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.launcher;
+
+import org.apache.brooklyn.launcher.BrooklynLauncher;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.File;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.entity.Application;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.rebind.RebindTestUtils;
+import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.ha.HighAvailabilityMode;
+import brooklyn.management.ha.ManagementPlaneSyncRecordPersister;
+import brooklyn.management.internal.ManagementContextInternal;
+import brooklyn.test.Asserts;
+import brooklyn.test.entity.LocalManagementContextForTests;
+import brooklyn.test.entity.TestApplication;
+import brooklyn.util.os.Os;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Files;
+
+public class BrooklynLauncherHighAvailabilityTest {
+
+    private static final Logger log = LoggerFactory.getLogger(BrooklynLauncherHighAvailabilityTest.class);
+    
+    private static final Duration TIMEOUT = Duration.THIRTY_SECONDS;
+    
+    private BrooklynLauncher primary;
+    private BrooklynLauncher secondary;
+    private BrooklynLauncher tertiary;
+    private File persistenceDir;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        persistenceDir = Files.createTempDir();
+        Os.deleteOnExitRecursively(persistenceDir);
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (primary != null) primary.terminate();
+        primary = null;
+        if (secondary != null) secondary.terminate();
+        secondary = null;
+        if (tertiary != null) tertiary.terminate();
+        tertiary = null;
+        if (persistenceDir != null) RebindTestUtils.deleteMementoDir(persistenceDir);
+        persistenceDir = null;
+    }
+    
+    @Test
+    public void testStandbyTakesOverWhenPrimaryTerminatedGracefully() throws Exception {
+        doTestStandbyTakesOver(true);
+    }
+
+    @Test(invocationCount=10, groups="Integration")
+    /** test issues with termination and promotion; 
+     * previously we got FileNotFound errors, though these should be fixed with
+     * the various PersistenceObjectStore prepare methods */
+    public void testStandbyTakesOverWhenPrimaryTerminatedGracefullyManyTimes() throws Exception {
+        testStandbyTakesOverWhenPrimaryTerminatedGracefully();
+    }
+
+    @Test(groups="Integration") // because slow waiting for timeouts to promote standbys
+    public void testStandbyTakesOverWhenPrimaryFails() throws Exception {
+        doTestStandbyTakesOver(false);
+    }
+    
+    protected void doTestStandbyTakesOver(boolean stopGracefully) throws Exception {
+        log.info("STARTING standby takeover test");
+        primary = BrooklynLauncher.newInstance();
+        primary.webconsole(false)
+                .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
+                .highAvailabilityMode(HighAvailabilityMode.AUTO)
+                .persistMode(PersistMode.AUTO)
+                .persistenceDir(persistenceDir)
+                .persistPeriod(Duration.millis(10))
+                .haHeartbeatPeriod(Duration.millis(10))
+                .haHeartbeatTimeout(Duration.millis(1000))
+                .application(EntitySpec.create(TestApplication.class))
+                .start();
+        ManagementContext primaryManagementContext = primary.getServerDetails().getManagementContext();
+        log.info("started mgmt primary "+primaryManagementContext);
+        
+        assertOnlyApp(primary.getServerDetails().getManagementContext(), TestApplication.class);
+        primaryManagementContext.getRebindManager().getPersister().waitForWritesCompleted(TIMEOUT);
+        
+        // Secondary will come up as standby
+        secondary = BrooklynLauncher.newInstance();
+        secondary.webconsole(false)
+                .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
+                .highAvailabilityMode(HighAvailabilityMode.AUTO)
+                .persistMode(PersistMode.AUTO)
+                .persistenceDir(persistenceDir)
+                .persistPeriod(Duration.millis(10))
+                .haHeartbeatPeriod(Duration.millis(10))
+                .haHeartbeatTimeout(Duration.millis(1000))
+                .start();
+        ManagementContext secondaryManagementContext = secondary.getServerDetails().getManagementContext();
+        log.info("started mgmt secondary "+secondaryManagementContext);
+        
+        // TODO can assert it sees the apps read only
+//        assertNoApps(secondary.getServerDetails().getManagementContext());
+
+        // Terminate primary; expect secondary to take over
+        if (stopGracefully) {
+            ((ManagementContextInternal)primaryManagementContext).terminate();
+        } else {
+            ManagementPlaneSyncRecordPersister planePersister = ((ManagementContextInternal)primaryManagementContext).getHighAvailabilityManager().getPersister();
+            planePersister.stop(); // can no longer write heartbeats
+            ((ManagementContextInternal)primaryManagementContext).terminate();
+        }
+        
+        assertOnlyAppEventually(secondaryManagementContext, TestApplication.class);
+        
+        // Start tertiary (force up as standby)
+        tertiary = BrooklynLauncher.newInstance();
+        tertiary.webconsole(false)
+                .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
+                .highAvailabilityMode(HighAvailabilityMode.STANDBY)
+                .persistMode(PersistMode.AUTO)
+                .persistenceDir(persistenceDir)
+                .persistPeriod(Duration.millis(10))
+                .haHeartbeatPeriod(Duration.millis(10))
+                .haHeartbeatTimeout(Duration.millis(1000))
+                .start();
+        ManagementContext tertiaryManagementContext = tertiary.getServerDetails().getManagementContext();
+        log.info("started mgmt tertiary "+primaryManagementContext);
+        
+        assertNoApps(tertiary.getServerDetails().getManagementContext());
+
+        // Terminate secondary; expect tertiary to take over
+        if (stopGracefully) {
+            ((ManagementContextInternal)secondaryManagementContext).terminate();
+        } else {
+            ManagementPlaneSyncRecordPersister planePersister = ((ManagementContextInternal)secondaryManagementContext).getHighAvailabilityManager().getPersister();
+            planePersister.stop(); // can no longer write heartbeats
+            ((ManagementContextInternal)secondaryManagementContext).terminate();
+        }
+        
+        assertOnlyAppEventually(tertiaryManagementContext, TestApplication.class);
+    }
+    
+    public void testHighAvailabilityMasterModeFailsIfAlreadyHasMaster() throws Exception {
+        primary = BrooklynLauncher.newInstance();
+        primary.webconsole(false)
+                .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
+                .highAvailabilityMode(HighAvailabilityMode.AUTO)
+                .persistMode(PersistMode.AUTO)
+                .persistenceDir(persistenceDir)
+                .persistPeriod(Duration.millis(10))
+                .application(EntitySpec.create(TestApplication.class))
+                .start();
+
+        try {
+            // Secondary will come up as standby
+            secondary = BrooklynLauncher.newInstance();
+            secondary.webconsole(false)
+                    .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
+                    .highAvailabilityMode(HighAvailabilityMode.MASTER)
+                    .persistMode(PersistMode.AUTO)
+                    .persistenceDir(persistenceDir)
+                    .persistPeriod(Duration.millis(10))
+                    .start();
+            fail();
+        } catch (IllegalStateException e) {
+            // success
+        }
+    }
+    
+    @Test
+    public void testHighAvailabilityStandbyModeFailsIfNoExistingMaster() throws Exception {
+        try {
+            primary = BrooklynLauncher.newInstance();
+            primary.webconsole(false)
+                    .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
+                    .highAvailabilityMode(HighAvailabilityMode.STANDBY)
+                    .persistMode(PersistMode.AUTO)
+                    .persistenceDir(persistenceDir)
+                    .persistPeriod(Duration.millis(10))
+                    .ignorePersistenceErrors(false)
+                    .application(EntitySpec.create(TestApplication.class))
+                    .start();
+            fail();
+        } catch (IllegalStateException e) {
+            // success
+        }
+    }
+    
+    @Test
+    public void testHighAvailabilityHotStandbyModeFailsIfNoExistingMaster() throws Exception {
+        try {
+            primary = BrooklynLauncher.newInstance();
+            primary.webconsole(false)
+                    .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
+                    .highAvailabilityMode(HighAvailabilityMode.HOT_STANDBY)
+                    .persistMode(PersistMode.AUTO)
+                    .persistenceDir(persistenceDir)
+                    .persistPeriod(Duration.millis(10))
+                    .ignorePersistenceErrors(false)
+                    .application(EntitySpec.create(TestApplication.class))
+                    .start();
+            fail();
+        } catch (IllegalStateException e) {
+            // success
+        }
+    }
+    
+    private void assertOnlyApp(ManagementContext managementContext, Class<? extends Application> expectedType) {
+        assertEquals(managementContext.getApplications().size(), 1, "apps="+managementContext.getApplications());
+        assertNotNull(Iterables.find(managementContext.getApplications(), Predicates.instanceOf(TestApplication.class), null), "apps="+managementContext.getApplications());
+    }
+    
+    private void assertNoApps(ManagementContext managementContext) {
+        if (!managementContext.getApplications().isEmpty())
+            log.warn("FAILED assertion (rethrowing), apps="+managementContext.getApplications());
+        assertTrue(managementContext.getApplications().isEmpty(), "apps="+managementContext.getApplications());
+    }
+    
+    private void assertOnlyAppEventually(final ManagementContext managementContext, final Class<? extends Application> expectedType) {
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertOnlyApp(managementContext, expectedType);
+            }});
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6f58ef3e/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogTest.java
new file mode 100644
index 0000000..48fb538
--- /dev/null
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.launcher;
+
+import java.io.File;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+
+import org.apache.brooklyn.catalog.BrooklynCatalog;
+import org.apache.brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogInitialization;
+import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.test.entity.LocalManagementContextForTests;
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.os.Os;
+
+public class BrooklynLauncherRebindCatalogTest {
+
+    private static final String TEST_VERSION = "test-version";
+    private static final String CATALOG_INITIAL = "classpath://rebind-test-catalog.bom";
+    private static final String CATALOG_ADDITIONS = "rebind-test-catalog-additions.bom";
+    private static final Iterable<String> EXPECTED_DEFAULT_IDS = ImmutableSet.of("one:" + TEST_VERSION, "two:" + TEST_VERSION);
+    private static final Iterable<String> EXPECTED_ADDED_IDS = ImmutableSet.of("three:" + TEST_VERSION, "four:" + TEST_VERSION);
+
+    private List<BrooklynLauncher> launchers = Lists.newCopyOnWriteArrayList();
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        for (BrooklynLauncher launcher : launchers) {
+            launcher.terminate();
+        }
+        launchers.clear();
+    }
+    
+    private BrooklynLauncher newLauncherForTests(String persistenceDir) {
+        CatalogInitialization catalogInitialization = new CatalogInitialization(CATALOG_INITIAL, false, null, false);
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .brooklynProperties(LocalManagementContextForTests.builder(true).buildProperties())
+                .catalogInitialization(catalogInitialization)
+                .persistMode(PersistMode.AUTO)
+                .persistenceDir(persistenceDir)
+                .webconsole(false);
+        launchers.add(launcher);
+        return launcher;
+    }
+
+    @Test
+    public void testRebindDoesNotEffectCatalog() {
+        String persistenceDir = newTempPersistenceContainerName();
+
+        BrooklynLauncher launcher = newLauncherForTests(persistenceDir);
+        launcher.start();
+        BrooklynCatalog catalog = launcher.getServerDetails().getManagementContext().getCatalog();
+
+        assertCatalogConsistsOfIds(catalog.getCatalogItems(), EXPECTED_DEFAULT_IDS);
+
+        catalog.deleteCatalogItem("one", TEST_VERSION);
+        catalog.deleteCatalogItem("two", TEST_VERSION);
+
+        Assert.assertEquals(Iterables.size(catalog.getCatalogItems()), 0);
+
+        catalog.addItems(new ResourceUtils(this).getResourceAsString(CATALOG_ADDITIONS));
+
+        assertCatalogConsistsOfIds(catalog.getCatalogItems(), EXPECTED_ADDED_IDS);
+
+        launcher.terminate();
+
+        BrooklynLauncher newLauncher = newLauncherForTests(persistenceDir);
+        newLauncher.start();
+        assertCatalogConsistsOfIds(newLauncher.getServerDetails().getManagementContext().getCatalog().getCatalogItems(), EXPECTED_ADDED_IDS);
+    }
+
+    private void assertCatalogConsistsOfIds(Iterable<CatalogItem<Object, Object>> catalogItems, Iterable<String> ids) {
+        Iterable<String> idsFromItems = Iterables.transform(catalogItems, new Function<CatalogItem<?,?>, String>() {
+            @Nullable
+            @Override
+            public String apply(CatalogItem<?, ?> catalogItem) {
+                return catalogItem.getCatalogItemId();
+            }
+        });
+        Assert.assertTrue(Iterables.elementsEqual(ids, idsFromItems), String.format("Expected %s, found %s", ids, idsFromItems));
+    }
+
+    protected String newTempPersistenceContainerName() {
+        File persistenceDirF = Files.createTempDir();
+        Os.deleteOnExitRecursively(persistenceDirF);
+        return persistenceDirF.getAbsolutePath();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6f58ef3e/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindTestFixture.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindTestFixture.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindTestFixture.java
new file mode 100644
index 0000000..bd804b6
--- /dev/null
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindTestFixture.java
@@ -0,0 +1,257 @@
+/*
+ * 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.launcher;
+
+import org.apache.brooklyn.launcher.BrooklynLauncher;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.BrooklynServerConfig;
+import brooklyn.entity.Application;
+import brooklyn.entity.basic.EntityPredicates;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
+import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+import brooklyn.location.Location;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.ha.HighAvailabilityMode;
+import brooklyn.test.Asserts;
+import brooklyn.test.entity.LocalManagementContextForTests;
+import brooklyn.test.entity.TestApplication;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.exceptions.FatalConfigurationRuntimeException;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+public abstract class BrooklynLauncherRebindTestFixture {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(BrooklynLauncherRebindTestFixture.class);
+    
+    protected String persistenceDir;
+    protected String persistenceLocationSpec;
+    protected List<BrooklynLauncher> launchers = MutableList.of();
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        persistenceDir = newTempPersistenceContainerName();
+    }
+    
+    protected abstract String newTempPersistenceContainerName();
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        for (BrooklynLauncher l: launchers) {
+            if (l.isStarted()) {
+                l.terminate();
+                PersistenceObjectStore store = getPersistenceStore(l.getServerDetails().getManagementContext());
+                if (store!=null) store.deleteCompletely();
+            }
+        }
+    }
+
+    protected BrooklynLauncher newLauncherBase() {
+        BrooklynLauncher l = BrooklynLauncher.newInstance()
+            .webconsole(false);
+        launchers.add(l);
+        return l;
+    }
+    protected BrooklynLauncher newLauncherDefault(PersistMode mode) {
+        return newLauncherBase()
+                .managementContext(newManagementContextForTests(null))
+                .persistMode(mode)
+                .persistenceDir(persistenceDir)
+                .persistPeriod(Duration.millis(10));
+    }
+    protected LocalManagementContextForTests newManagementContextForTests(BrooklynProperties props) {
+        if (props==null)
+            return new LocalManagementContextForTests();
+        else
+            return new LocalManagementContextForTests(props);
+    }
+
+    protected ManagementContext lastMgmt() {
+        return Iterables.getLast(launchers).getServerDetails().getManagementContext();
+    }
+    
+    @Test
+    public void testRebindsToExistingApp() throws Exception {
+        populatePersistenceDir(persistenceDir, EntitySpec.create(TestApplication.class).displayName("myorig"));
+        
+        // Rebind to the app we just started last time
+        
+        newLauncherDefault(PersistMode.REBIND).start();
+        
+        assertOnlyApp(lastMgmt(), TestApplication.class);
+        assertNotNull(Iterables.find(lastMgmt().getApplications(), EntityPredicates.displayNameEqualTo("myorig"), null), "apps="+lastMgmt().getApplications());
+    }
+
+    @Test
+    public void testRebindCanAddNewApps() throws Exception {
+        populatePersistenceDir(persistenceDir, EntitySpec.create(TestApplication.class).displayName("myorig"));
+        
+        // Rebind to the app we started last time
+        newLauncherDefault(PersistMode.REBIND)
+                .application(EntitySpec.create(TestApplication.class).displayName("mynew"))
+                .start();
+        
+        // New app was added, and orig app was rebound
+        assertEquals(lastMgmt().getApplications().size(), 2, "apps="+lastMgmt().getApplications());
+        assertNotNull(Iterables.find(lastMgmt().getApplications(), EntityPredicates.displayNameEqualTo("mynew"), null), "apps="+lastMgmt().getApplications());
+
+        // And subsequently can create new apps
+        StartableApplication app3 = lastMgmt().getEntityManager().createEntity(
+                EntitySpec.create(TestApplication.class).displayName("mynew2"));
+        app3.start(ImmutableList.<Location>of());
+    }
+
+    @Test
+    public void testAutoRebindsToExistingApp() throws Exception {
+        EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class);
+        populatePersistenceDir(persistenceDir, appSpec);
+        
+        // Auto will rebind if the dir exists
+        newLauncherDefault(PersistMode.AUTO).start();
+        
+        assertOnlyApp(lastMgmt(), TestApplication.class);
+    }
+
+    @Test
+    public void testCleanDoesNotRebindToExistingApp() throws Exception {
+        EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class);
+        populatePersistenceDir(persistenceDir, appSpec);
+        
+        // Auto will rebind if the dir exists
+        newLauncherDefault(PersistMode.CLEAN).start();
+        
+        assertTrue(lastMgmt().getApplications().isEmpty(), "apps="+lastMgmt().getApplications());
+    }
+
+    @Test
+    public void testAutoRebindCreatesNewIfEmptyDir() throws Exception {
+        // Auto will rebind if the dir exists
+        newLauncherDefault(PersistMode.AUTO)
+                .application(EntitySpec.create(TestApplication.class))
+                .start();
+        
+        assertOnlyApp(lastMgmt(), TestApplication.class);
+        assertMementoContainerNonEmptyForTypeEventually("entities");
+    }
+
+    @Test
+    public void testRebindRespectsPersistenceDirSetInProperties() throws Exception {
+        String persistenceDir2 = newTempPersistenceContainerName();
+        
+        BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newDefault();
+        brooklynProperties.put(BrooklynServerConfig.PERSISTENCE_DIR, persistenceDir2);
+        LocalManagementContextForTests mgmt = newManagementContextForTests(brooklynProperties);
+        
+        // Rebind to the app we started last time
+        newLauncherBase()
+                .persistMode(PersistMode.AUTO)
+                .persistPeriod(Duration.millis(10))
+                .managementContext(mgmt)
+                .start();
+        
+        checkPersistenceContainerNameIs(persistenceDir2);
+    }
+
+    // assumes default persistence dir is rebindable
+    @Test(groups="Integration")
+    public void testRebindRespectsDefaultPersistenceDir() throws Exception {
+        newLauncherDefault(PersistMode.AUTO)
+                .persistenceDir((String)null)
+                .start();
+        
+        checkPersistenceContainerNameIsDefault();
+    }
+    
+    protected abstract void checkPersistenceContainerNameIsDefault();
+    protected abstract void checkPersistenceContainerNameIs(String expected);
+
+    @Test
+    public void testPersistenceFailsIfNoDir() throws Exception {
+        runRebindFails(PersistMode.REBIND, badContainerName(), "does not exist");
+    }
+
+    protected abstract String badContainerName();
+
+    @Test
+    public void testExplicitRebindFailsIfEmpty() throws Exception {
+        runRebindFails(PersistMode.REBIND, persistenceDir, "directory is empty");
+    }
+
+    protected void runRebindFails(PersistMode persistMode, String dir, String errmsg) throws Exception {
+        try {
+            newLauncherDefault(persistMode)
+                    .persistenceDir(dir)
+                    .start();
+        } catch (FatalConfigurationRuntimeException e) {
+            if (!e.toString().contains(errmsg)) {
+                throw e;
+            }
+        }
+    }
+
+    protected void populatePersistenceDir(String dir, EntitySpec<? extends StartableApplication> appSpec) throws Exception {
+        BrooklynLauncher launcher = newLauncherDefault(PersistMode.CLEAN)
+                .highAvailabilityMode(HighAvailabilityMode.MASTER)
+                .persistenceDir(dir)
+                .application(appSpec)
+                .start();
+        launcher.terminate();
+        assertMementoContainerNonEmptyForTypeEventually("entities");
+    }
+    
+    protected void assertOnlyApp(ManagementContext managementContext, Class<? extends Application> expectedType) {
+        assertEquals(managementContext.getApplications().size(), 1, "apps="+managementContext.getApplications());
+        assertNotNull(Iterables.find(managementContext.getApplications(), Predicates.instanceOf(TestApplication.class), null), "apps="+managementContext.getApplications());
+    }
+    
+    protected void assertMementoContainerNonEmptyForTypeEventually(final String type) {
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", Duration.TEN_SECONDS), new Runnable() {
+            @Override public void run() {
+                getPersistenceStore(lastMgmt()).listContentsWithSubPath(type);
+            }});
+    }
+
+    static PersistenceObjectStore getPersistenceStore(ManagementContext managementContext) {
+        if (managementContext==null) return null;
+        BrooklynMementoPersisterToObjectStore persister = (BrooklynMementoPersisterToObjectStore)managementContext.getRebindManager().getPersister();
+        if (persister==null) return null;
+        return persister.getObjectStore();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6f58ef3e/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindTestToFiles.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindTestToFiles.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindTestToFiles.java
new file mode 100644
index 0000000..ee07f67
--- /dev/null
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindTestToFiles.java
@@ -0,0 +1,154 @@
+/*
+ * 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.launcher;
+
+import org.apache.brooklyn.launcher.BrooklynLauncher;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.File;
+
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.BrooklynServerPaths;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
+import brooklyn.entity.rebind.persister.FileBasedObjectStore;
+import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.ha.HighAvailabilityMode;
+import brooklyn.test.entity.TestApplication;
+import brooklyn.util.javalang.JavaClassNames;
+import brooklyn.util.os.Os;
+import brooklyn.util.text.Identifiers;
+
+import com.google.common.base.Joiner;
+import com.google.common.io.Files;
+
+public class BrooklynLauncherRebindTestToFiles extends BrooklynLauncherRebindTestFixture {
+
+    protected String newTempPersistenceContainerName() {
+        File persistenceDirF = Files.createTempDir();
+        Os.deleteOnExitRecursively(persistenceDirF);
+        return persistenceDirF.getAbsolutePath();
+    }
+    
+    protected String badContainerName() {
+        return "/path/does/not/exist/"+Identifiers.makeRandomId(4);
+    }
+    
+    protected void checkPersistenceContainerNameIs(String expected) {
+        String expectedFqp = new File(Os.tidyPath(expected)).getAbsolutePath();
+        assertEquals(getPersistenceDir(lastMgmt()).getAbsolutePath(), expectedFqp);
+    }
+
+    static File getPersistenceDir(ManagementContext managementContext) {
+        BrooklynMementoPersisterToObjectStore persister = (BrooklynMementoPersisterToObjectStore)managementContext.getRebindManager().getPersister();
+        FileBasedObjectStore store = (FileBasedObjectStore)persister.getObjectStore();
+        return store.getBaseDir();
+    }
+
+    protected void checkPersistenceContainerNameIsDefault() {
+        String expected = BrooklynServerPaths.newMainPersistencePathResolver(BrooklynProperties.Factory.newEmpty()).location(null).dir(null).resolve();
+        checkPersistenceContainerNameIs(expected);
+    }
+
+    @Test
+    public void testPersistenceFailsIfIsFile() throws Exception {
+        File tempF = File.createTempFile("test-"+JavaClassNames.niceClassAndMethod(), ".not_dir");
+        tempF.deleteOnExit();
+        String tempFileName = tempF.getAbsolutePath();
+        
+        try {
+            runRebindFails(PersistMode.AUTO, tempFileName, "must not be a file");
+            runRebindFails(PersistMode.REBIND, tempFileName, "must not be a file");
+            runRebindFails(PersistMode.CLEAN, tempFileName, "must not be a file");
+        } finally {
+            new File(tempFileName).delete();
+        }
+    }
+    
+    @Test
+    public void testPersistenceFailsIfNotWritable() throws Exception {
+        EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class);
+        populatePersistenceDir(persistenceDir, appSpec);
+        new File(persistenceDir).setWritable(false);
+        try {
+            runRebindFails(PersistMode.AUTO, persistenceDir, "not writable");
+            runRebindFails(PersistMode.REBIND, persistenceDir, "not writable");
+            runRebindFails(PersistMode.CLEAN, persistenceDir, "not writable");
+        } finally {
+            new File(persistenceDir).setWritable(true);
+        }
+    }
+
+    @Test
+    public void testPersistenceFailsIfNotReadable() throws Exception {
+        EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class);
+        populatePersistenceDir(persistenceDir, appSpec);
+        new File(persistenceDir).setReadable(false);
+        try {
+            runRebindFails(PersistMode.AUTO, persistenceDir, "not readable");
+            runRebindFails(PersistMode.REBIND, persistenceDir, "not readable");
+            runRebindFails(PersistMode.CLEAN, persistenceDir, "not readable");
+        } finally {
+            new File(persistenceDir).setReadable(true);
+        }
+    }
+
+    @Test(groups="Integration")
+    public void testCopyPersistedState() throws Exception {
+        EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class);
+        populatePersistenceDir(persistenceDir, appSpec);
+
+        File destinationDir = Files.createTempDir();
+        String destination = destinationDir.getAbsolutePath();
+        String destinationLocation = null; // i.e. file system, rather than object store
+        try {
+            // Auto will rebind if the dir exists
+            BrooklynLauncher launcher = newLauncherDefault(PersistMode.AUTO)
+                    .highAvailabilityMode(HighAvailabilityMode.MASTER)
+                    .webconsole(false);
+            launcher.copyPersistedState(destination, destinationLocation);
+            launcher.terminate();
+            
+            File entities = new File(Os.mergePaths(destination), "entities");
+            assertTrue(entities.isDirectory(), "entities directory should exist");
+            assertEquals(entities.listFiles().length, 1, "entities directory should contain one file (contained: "+
+                    Joiner.on(", ").join(entities.listFiles()) +")");
+
+            File nodes = new File(Os.mergePaths(destination, "nodes"));
+            assertTrue(nodes.isDirectory(), "nodes directory should exist");
+            assertNotEquals(nodes.listFiles().length, 0, "nodes directory should not be empty");
+
+            // Should now have a usable copy in the destinationDir
+            // Auto will rebind if the dir exists
+            newLauncherDefault(PersistMode.AUTO)
+                    .webconsole(false)
+                    .persistenceDir(destinationDir)
+                    .start();
+            assertOnlyApp(lastMgmt(), TestApplication.class);
+            
+        } finally {
+            Os.deleteRecursively(destinationDir);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6f58ef3e/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindToCloudObjectStoreTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindToCloudObjectStoreTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindToCloudObjectStoreTest.java
new file mode 100644
index 0000000..b2aeaf9
--- /dev/null
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindToCloudObjectStoreTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.launcher;
+
+import org.apache.brooklyn.launcher.BrooklynLauncher;
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.BrooklynServerPaths;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
+import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.entity.rebind.persister.jclouds.BlobStoreTest;
+import brooklyn.entity.rebind.persister.jclouds.JcloudsBlobStoreBasedObjectStore;
+import brooklyn.management.ManagementContext;
+import brooklyn.mementos.BrooklynMementoRawData;
+import brooklyn.test.entity.LocalManagementContextForTests;
+import brooklyn.test.entity.TestApplication;
+import brooklyn.util.javalang.JavaClassNames;
+import brooklyn.util.os.Os;
+import brooklyn.util.text.Identifiers;
+
+@Test(groups="Live")
+public class BrooklynLauncherRebindToCloudObjectStoreTest extends BrooklynLauncherRebindTestFixture {
+
+    // FIXME BrooklynLauncherRebindToCloudObjectStoreTest.testCleanDoesNotRebindToExistingApp failed:
+    //     apps=[Application[mDNfOA7w]] expected [true] but found [false]
+    // Should it really delete everything in the bucket?! Only if we can back up first!
+
+    // FIXME brooklyn.util.exceptions.FatalRuntimeException: Error rebinding to persisted state: Writes not allowed in brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore@7d2f7563
+    //     at brooklyn.launcher.BrooklynLauncher.persistState(BrooklynLauncher.java:502)
+    //     at brooklyn.launcher.BrooklynLauncherRebindToCloudObjectStoreTest.testCopyPersistedState(BrooklynLauncherRebindToCloudObjectStoreTest.java:144)
+    // Presumably a previous run wasn't tearing down properly, so it joined as a standby rather than being master?! 
+    
+    { persistenceLocationSpec = BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC; }
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        persistenceDir = newTempPersistenceContainerName();
+    }
+
+    @Override
+    protected BrooklynLauncher newLauncherBase() {
+        return super.newLauncherBase().persistenceLocation(persistenceLocationSpec);
+    }
+    
+    protected LocalManagementContextForTests newManagementContextForTests(BrooklynProperties props) {
+        BrooklynProperties p2 = BrooklynProperties.Factory.newDefault();
+        if (props!=null) p2.putAll(props);
+        return new LocalManagementContextForTests(p2);
+    }
+
+    @Override
+    protected String newTempPersistenceContainerName() {
+        return "test-"+JavaClassNames.callerStackElement(0).getClassName()+"-"+Identifiers.makeRandomId(4);
+    }
+    
+    protected String badContainerName() {
+        return "container-does-not-exist-"+Identifiers.makeRandomId(4);
+    }
+    
+    protected void checkPersistenceContainerNameIs(String expected) {
+        assertEquals(getPersistenceContainerName(lastMgmt()), expected);
+    }
+
+    static String getPersistenceContainerName(ManagementContext managementContext) {
+        BrooklynMementoPersisterToObjectStore persister = (BrooklynMementoPersisterToObjectStore)managementContext.getRebindManager().getPersister();
+        JcloudsBlobStoreBasedObjectStore store = (JcloudsBlobStoreBasedObjectStore)persister.getObjectStore();
+        return store.getContainerName();
+    }
+
+    protected void checkPersistenceContainerNameIsDefault() {
+        checkPersistenceContainerNameIs(BrooklynServerPaths.DEFAULT_PERSISTENCE_CONTAINER_NAME);
+    }
+
+    @Override @Test(groups="Live")
+    public void testRebindsToExistingApp() throws Exception {
+        super.testRebindsToExistingApp();
+    }
+
+    @Override @Test(groups="Live")
+    public void testRebindCanAddNewApps() throws Exception {
+        super.testRebindCanAddNewApps();
+    }
+
+    @Override @Test(groups="Live")
+    public void testAutoRebindsToExistingApp() throws Exception {
+        super.testAutoRebindsToExistingApp();
+    }
+
+    // TODO Marked as work-in-progress because "clean" does not backup and then clean out the existing
+    // object store's bucket. Unclear what best behaviour there should be: should we really delete
+    // the data?! We better be confident about our backup!
+    @Override @Test(groups={"Live", "WIP"})
+    public void testCleanDoesNotRebindToExistingApp() throws Exception {
+        super.testCleanDoesNotRebindToExistingApp();
+    }
+
+    @Override @Test(groups="Live")
+    public void testAutoRebindCreatesNewIfEmptyDir() throws Exception {
+        super.testAutoRebindCreatesNewIfEmptyDir();
+    }
+
+    @Override @Test(groups="Live")
+    public void testRebindRespectsPersistenceDirSetInProperties() throws Exception {
+        super.testRebindRespectsPersistenceDirSetInProperties();
+    }
+
+    @Override @Test(groups="Live")
+    public void testRebindRespectsDefaultPersistenceDir() throws Exception {
+        super.testRebindRespectsDefaultPersistenceDir();
+    }
+
+    @Override @Test(groups="Live")
+    public void testPersistenceFailsIfNoDir() throws Exception {
+        super.testPersistenceFailsIfNoDir();
+    }
+
+    @Override @Test(groups="Live")
+    public void testExplicitRebindFailsIfEmpty() throws Exception {
+        super.testExplicitRebindFailsIfEmpty();
+    }
+
+    // TODO Remove duplication from BrooklynLauncherRebindTestToFiles.testCopyPersistedState()
+    @Test(groups="Live")
+    public void testCopyPersistedState() throws Exception {
+        EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class);
+        populatePersistenceDir(persistenceDir, appSpec);
+        
+        String destinationDir = newTempPersistenceContainerName();
+        String destinationLocation = persistenceLocationSpec;
+        try {
+            // Auto will rebind if the dir exists
+            BrooklynLauncher launcher = newLauncherDefault(PersistMode.AUTO)
+                    .webconsole(false)
+                    .persistenceLocation(persistenceLocationSpec);
+            BrooklynMementoRawData memento = launcher.retrieveState();
+            launcher.persistState(memento, destinationDir, destinationLocation);
+            launcher.terminate();
+            
+            assertEquals(memento.getEntities().size(), 1, "entityMementos="+memento.getEntities().keySet());
+            
+            // Should now have a usable copy in the destionationDir
+            // Auto will rebind if the dir exists
+            newLauncherDefault(PersistMode.AUTO)
+                    .webconsole(false)
+                    .persistenceDir(destinationDir)
+                    .persistenceLocation(destinationLocation)
+                    .start();
+            assertOnlyApp(lastMgmt(), TestApplication.class);
+            
+        } finally {
+            Os.deleteRecursively(destinationDir);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6f58ef3e/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java
new file mode 100644
index 0000000..df295d8
--- /dev/null
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java
@@ -0,0 +1,368 @@
+/*
+ * 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.launcher;
+
+import org.apache.brooklyn.launcher.BrooklynLauncher;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.util.Properties;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.catalog.internal.CatalogInitialization;
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.BrooklynServerConfig;
+import brooklyn.entity.Application;
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.rebind.RebindTestUtils;
+import brooklyn.location.Location;
+import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.management.internal.ManagementContextInternal;
+import brooklyn.test.HttpTestUtils;
+import brooklyn.test.entity.LocalManagementContextForTests;
+import brooklyn.test.entity.TestApplication;
+import brooklyn.test.entity.TestApplicationImpl;
+import brooklyn.test.entity.TestEntity;
+import brooklyn.util.exceptions.FatalRuntimeException;
+import brooklyn.util.io.FileUtil;
+import brooklyn.util.net.Urls;
+import brooklyn.util.os.Os;
+import brooklyn.util.text.StringFunctions;
+import brooklyn.util.text.Strings;
+
+import com.google.api.client.util.Preconditions;
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.io.Files;
+
+public class BrooklynLauncherTest {
+    
+    private BrooklynLauncher launcher;
+    private File persistenceDir;
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (launcher != null) launcher.terminate();
+        if (persistenceDir != null) RebindTestUtils.deleteMementoDir(persistenceDir);
+        launcher = null;
+    }
+    
+    // Integration because takes a few seconds to start web-console
+    @Test(groups="Integration")
+    public void testStartsWebServerOnExpectectedPort() throws Exception {
+        launcher = newLauncherForTests(true)
+                .webconsolePort("10000+")
+                .start();
+        
+        String webServerUrlStr = launcher.getServerDetails().getWebServerUrl();
+        URI webServerUri = new URI(webServerUrlStr);
+        
+        assertEquals(launcher.getApplications(), ImmutableList.of());
+        assertTrue(webServerUri.getPort() >= 10000 && webServerUri.getPort() < 10100, "port="+webServerUri.getPort()+"; uri="+webServerUri);
+        HttpTestUtils.assertUrlReachable(webServerUrlStr);
+    }
+    
+    // Integration because takes a few seconds to start web-console
+    @Test(groups="Integration")
+    public void testWebServerTempDirRespectsDataDirConfig() throws Exception {
+        String dataDirName = ".brooklyn-foo"+Strings.makeRandomId(4);
+        String dataDir = "~/"+dataDirName;
+
+        launcher = newLauncherForTests(true)
+                .brooklynProperties(BrooklynServerConfig.MGMT_BASE_DIR, dataDir)
+                .start();
+        
+        ManagementContext managementContext = launcher.getServerDetails().getManagementContext();
+        String expectedTempDir = Os.mergePaths(Os.home(), dataDirName, "planes", managementContext.getManagementPlaneId(), managementContext.getManagementNodeId(), "jetty");
+        
+        File webappTempDir = launcher.getServerDetails().getWebServer().getWebappTempDir();
+        assertEquals(webappTempDir.getAbsolutePath(), expectedTempDir);
+    }
+    
+    @Test
+    public void testCanDisableWebServerStartup() throws Exception {
+        launcher = newLauncherForTests(true)
+                .webconsole(false)
+                .start();
+        
+        assertNull(launcher.getServerDetails().getWebServer());
+        assertNull(launcher.getServerDetails().getWebServerUrl());
+        Assert.assertTrue( ((ManagementContextInternal)launcher.getServerDetails().getManagementContext()).errors().isEmpty() );
+    }
+    
+    @Test
+    public void testStartsAppInstance() throws Exception {
+        launcher = newLauncherForTests(true)
+                .webconsole(false)
+                .application(new TestApplicationImpl())
+                .start();
+        
+        assertOnlyApp(launcher, TestApplication.class);
+    }
+    
+    @Test
+    public void testStartsAppFromSpec() throws Exception {
+        launcher = newLauncherForTests(true)
+                .webconsole(false)
+                .application(EntitySpec.create(TestApplication.class))
+                .start();
+        
+        assertOnlyApp(launcher, TestApplication.class);
+    }
+    
+    @Test
+    public void testStartsAppFromBuilder() throws Exception {
+        launcher = newLauncherForTests(true)
+                .webconsole(false)
+                .application(new ApplicationBuilder(EntitySpec.create(TestApplication.class)) {
+                        @Override protected void doBuild() {
+                        }})
+                .start();
+        
+        assertOnlyApp(launcher, TestApplication.class);
+    }
+
+    @Test
+    public void testStartsAppFromYAML() throws Exception {
+        String yaml = "name: example-app\n" +
+                "services:\n" +
+                "- serviceType: brooklyn.test.entity.TestEntity\n" +
+                "  name: test-app";
+        launcher = newLauncherForTests(true)
+                .webconsole(false)
+                .application(yaml)
+                .start();
+
+        assertEquals(launcher.getApplications().size(), 1, "apps="+launcher.getApplications());
+        Application app = Iterables.getOnlyElement(launcher.getApplications());
+        assertEquals(app.getChildren().size(), 1, "children=" + app.getChildren());
+        assertTrue(Iterables.getOnlyElement(app.getChildren()) instanceof TestEntity);
+    }
+    
+    @Test  // may take 2s initializing location if running this test case alone, but noise if running suite 
+    public void testStartsAppInSuppliedLocations() throws Exception {
+        launcher = newLauncherForTests(true)
+                .webconsole(false)
+                .location("localhost")
+                .application(new ApplicationBuilder(EntitySpec.create(TestApplication.class)) {
+                        @Override protected void doBuild() {
+                        }})
+                .start();
+        
+        Application app = Iterables.find(launcher.getApplications(), Predicates.instanceOf(TestApplication.class));
+        assertOnlyLocation(app, LocalhostMachineProvisioningLocation.class);
+    }
+    
+    @Test
+    public void testUsesSuppliedManagementContext() throws Exception {
+        LocalManagementContext myManagementContext = LocalManagementContextForTests.newInstance();
+        launcher = newLauncherForTests(false)
+                .webconsole(false)
+                .managementContext(myManagementContext)
+                .start();
+        
+        assertSame(launcher.getServerDetails().getManagementContext(), myManagementContext);
+    }
+    
+    @Test
+    public void testUsesSuppliedBrooklynProperties() throws Exception {
+        BrooklynProperties props = LocalManagementContextForTests.builder(true).buildProperties();
+        props.put("mykey", "myval");
+        launcher = newLauncherForTests(false)
+                .webconsole(false)
+                .brooklynProperties(props)
+                .start();
+        
+        assertEquals(launcher.getServerDetails().getManagementContext().getConfig().getFirst("mykey"), "myval");
+    }
+
+    @Test
+    public void testUsesSupplementaryBrooklynProperties() throws Exception {
+        launcher = newLauncherForTests(true)
+                .webconsole(false)
+                .brooklynProperties("mykey", "myval")
+                .start();
+        
+        assertEquals(launcher.getServerDetails().getManagementContext().getConfig().getFirst("mykey"), "myval");
+    }
+    
+    @Test
+    public void testReloadBrooklynPropertiesRestoresProgrammaticProperties() throws Exception {
+        launcher = newLauncherForTests(true)
+                .webconsole(false)
+                .brooklynProperties("mykey", "myval")
+                .start();
+        LocalManagementContext managementContext = (LocalManagementContext)launcher.getServerDetails().getManagementContext();
+        assertEquals(managementContext.getConfig().getFirst("mykey"), "myval");
+        managementContext.getBrooklynProperties().put("mykey", "newval");
+        assertEquals(managementContext.getConfig().getFirst("mykey"), "newval");
+        managementContext.reloadBrooklynProperties();
+        assertEquals(managementContext.getConfig().getFirst("mykey"), "myval");
+    }
+    
+    @Test
+    public void testReloadBrooklynPropertiesFromFile() throws Exception {
+        File globalPropertiesFile = File.createTempFile("local-brooklyn-properties-test", ".properties");
+        FileUtil.setFilePermissionsTo600(globalPropertiesFile);
+        try {
+            String property = "mykey=myval";
+            Files.append(getMinimalLauncherPropertiesString()+property, globalPropertiesFile, Charsets.UTF_8);
+            launcher = newLauncherForTests(false)
+                    .webconsole(false)
+                    .globalBrooklynPropertiesFile(globalPropertiesFile.getAbsolutePath())
+                    .start();
+            LocalManagementContext managementContext = (LocalManagementContext)launcher.getServerDetails().getManagementContext();
+            assertEquals(managementContext.getConfig().getFirst("mykey"), "myval");
+            property = "mykey=newval";
+            Files.write(getMinimalLauncherPropertiesString()+property, globalPropertiesFile, Charsets.UTF_8);
+            managementContext.reloadBrooklynProperties();
+            assertEquals(managementContext.getConfig().getFirst("mykey"), "newval");
+        } finally {
+            globalPropertiesFile.delete();
+        }
+    }
+
+    @Test(groups="Integration")
+    public void testChecksGlobalBrooklynPropertiesPermissionsX00() throws Exception {
+        File propsFile = File.createTempFile("testChecksGlobalBrooklynPropertiesPermissionsX00", ".properties");
+        propsFile.setReadable(true, false);
+        try {
+            launcher = newLauncherForTests(false)
+                    .webconsole(false)
+                    .globalBrooklynPropertiesFile(propsFile.getAbsolutePath())
+                    .start();
+
+            Assert.fail("Should have thrown");
+        } catch (FatalRuntimeException e) {
+            if (!e.toString().contains("Invalid permissions for file")) throw e;
+        } finally {
+            propsFile.delete();
+        }
+    }
+
+    @Test(groups="Integration")
+    public void testChecksLocalBrooklynPropertiesPermissionsX00() throws Exception {
+        File propsFile = File.createTempFile("testChecksLocalBrooklynPropertiesPermissionsX00", ".properties");
+        propsFile.setReadable(true, false);
+        try {
+            launcher = newLauncherForTests(false)
+                    .webconsole(false)
+                    .localBrooklynPropertiesFile(propsFile.getAbsolutePath())
+                    .start();
+            
+            Assert.fail("Should have thrown");
+        } catch (FatalRuntimeException e) {
+            if (!e.toString().contains("Invalid permissions for file")) throw e;
+        } finally {
+            propsFile.delete();
+        }
+    }
+
+    @Test(groups="Integration")
+    public void testStartsWithBrooklynPropertiesPermissionsX00() throws Exception {
+        File globalPropsFile = File.createTempFile("testChecksLocalBrooklynPropertiesPermissionsX00_global", ".properties");
+        Files.write(getMinimalLauncherPropertiesString()+"key_in_global=1", globalPropsFile, Charset.defaultCharset());
+        File localPropsFile = File.createTempFile("testChecksLocalBrooklynPropertiesPermissionsX00_local", ".properties");
+        Files.write("key_in_local=2", localPropsFile, Charset.defaultCharset());
+        FileUtil.setFilePermissionsTo600(globalPropsFile);
+        FileUtil.setFilePermissionsTo600(localPropsFile);
+        try {
+            launcher = newLauncherForTests(false)
+                    .webconsole(false)
+                    .localBrooklynPropertiesFile(localPropsFile.getAbsolutePath())
+                    .globalBrooklynPropertiesFile(globalPropsFile.getAbsolutePath())
+                    .start();
+            assertEquals(launcher.getServerDetails().getManagementContext().getConfig().getFirst("key_in_global"), "1");
+            assertEquals(launcher.getServerDetails().getManagementContext().getConfig().getFirst("key_in_local"), "2");
+        } finally {
+            globalPropsFile.delete();
+            localPropsFile.delete();
+        }
+    }
+    
+    @Test  // takes a bit of time because starts webapp, but also tests rest api so useful
+    public void testErrorsCaughtByApiAndRestApiWorks() throws Exception {
+        launcher = newLauncherForTests(true)
+                .catalogInitialization(new CatalogInitialization(null, false, null, false).addPopulationCallback(new Function<CatalogInitialization, Void>() {
+                    @Override
+                    public Void apply(CatalogInitialization input) {
+                        throw new RuntimeException("deliberate-exception-for-testing");
+                    }
+                }))
+                .start();
+        // such an error should be thrown, then caught in this calling thread
+        ManagementContext mgmt = launcher.getServerDetails().getManagementContext();
+        Assert.assertFalse( ((ManagementContextInternal)mgmt).errors().isEmpty() );
+        Assert.assertTrue( ((ManagementContextInternal)mgmt).errors().get(0).toString().contains("deliberate"), ""+((ManagementContextInternal)mgmt).errors() );
+        HttpTestUtils.assertContentMatches(
+            Urls.mergePaths(launcher.getServerDetails().getWebServerUrl(), "v1/server/up"), 
+            "true");
+        HttpTestUtils.assertContentMatches(
+            Urls.mergePaths(launcher.getServerDetails().getWebServerUrl(), "v1/server/healthy"), 
+            "false");
+        // TODO test errors api?
+    }
+
+    private BrooklynLauncher newLauncherForTests(boolean minimal) {
+        Preconditions.checkArgument(launcher==null, "can only be used if no launcher yet");
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance();
+        if (minimal)
+            launcher.brooklynProperties(LocalManagementContextForTests.builder(true).buildProperties());
+        return launcher;
+    }
+
+    private String getMinimalLauncherPropertiesString() throws IOException {
+        BrooklynProperties p1 = LocalManagementContextForTests.builder(true).buildProperties();
+        Properties p = new Properties();
+        p.putAll(Maps.transformValues(p1.asMapWithStringKeys(), StringFunctions.toStringFunction()));
+        Writer w = new StringWriter();
+        p.store(w, "test");
+        w.close();
+        return w.toString()+"\n";
+    }
+
+    private void assertOnlyApp(BrooklynLauncher launcher, Class<? extends Application> expectedType) {
+        assertEquals(launcher.getApplications().size(), 1, "apps="+launcher.getApplications());
+        assertNotNull(Iterables.find(launcher.getApplications(), Predicates.instanceOf(TestApplication.class), null), "apps="+launcher.getApplications());
+    }
+    
+    private void assertOnlyLocation(Application app, Class<? extends Location> expectedType) {
+        assertEquals(app.getLocations().size(), 1, "locs="+app.getLocations());
+        assertNotNull(Iterables.find(app.getLocations(), Predicates.instanceOf(LocalhostMachineProvisioningLocation.class), null), "locs="+app.getLocations());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6f58ef3e/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynWebServerTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynWebServerTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynWebServerTest.java
new file mode 100644
index 0000000..9510754
--- /dev/null
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynWebServerTest.java
@@ -0,0 +1,210 @@
+/*
+ * 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.launcher;
+
+import org.apache.brooklyn.launcher.BrooklynWebServer;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.util.List;
+import java.util.Map;
+
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.entity.basic.Entities;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.rest.BrooklynWebConfig;
+import brooklyn.test.entity.LocalManagementContextForTests;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.http.HttpTool;
+import brooklyn.util.http.HttpToolResponse;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+
+public class BrooklynWebServerTest {
+
+    public static final Logger log = LoggerFactory.getLogger(BrooklynWebServer.class);
+
+    private BrooklynProperties brooklynProperties;
+    private BrooklynWebServer webServer;
+    private List<LocalManagementContext> managementContexts = Lists.newCopyOnWriteArrayList();
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp(){
+        brooklynProperties = BrooklynProperties.Factory.newEmpty();
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        for (LocalManagementContext managementContext : managementContexts) {
+            Entities.destroyAll(managementContext);
+        }
+        managementContexts.clear();
+        if (webServer != null) webServer.stop();
+    }
+    
+    private LocalManagementContext newManagementContext(BrooklynProperties brooklynProperties) {
+        LocalManagementContext result = new LocalManagementContextForTests(brooklynProperties);
+        managementContexts.add(result);
+        return result;
+    }
+    
+    @Test
+    public void verifyHttp() throws Exception {
+        webServer = new BrooklynWebServer(newManagementContext(brooklynProperties));
+        try {
+            webServer.start();
+    
+            HttpToolResponse response = HttpTool.execAndConsume(new DefaultHttpClient(), new HttpGet(webServer.getRootUrl()));
+            assertEquals(response.getResponseCode(), 200);
+        } finally {
+            webServer.stop();
+        }
+    }
+
+    @DataProvider(name="keystorePaths")
+    public Object[][] getKeystorePaths() {
+        return new Object[][] {
+                {getFile("server.ks")},
+                {new File(getFile("server.ks")).toURI().toString()},
+                {"classpath://server.ks"}};
+    }
+    
+    @Test(dataProvider="keystorePaths")
+    public void verifyHttps(String keystoreUrl) throws Exception {
+        Map<String,?> flags = ImmutableMap.<String,Object>builder()
+                .put("httpsEnabled", true)
+                .put("keystoreUrl", keystoreUrl)
+                .put("keystorePassword", "password")
+                .build();
+        webServer = new BrooklynWebServer(flags, newManagementContext(brooklynProperties));
+        webServer.start();
+        
+        try {
+            KeyStore keyStore = load("client.ks", "password");
+            KeyStore trustStore = load("client.ts", "password");
+            SSLSocketFactory socketFactory = new SSLSocketFactory(SSLSocketFactory.TLS, keyStore, "password", trustStore, (SecureRandom)null, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+            HttpToolResponse response = HttpTool.execAndConsume(
+                    HttpTool.httpClientBuilder()
+                            .port(webServer.getActualPort())
+                            .https(true)
+                            .socketFactory(socketFactory)
+                            .build(),
+                    new HttpGet(webServer.getRootUrl()));
+            assertEquals(response.getResponseCode(), 200);
+        } finally {
+            webServer.stop();
+        }
+    }
+
+    @Test
+    public void verifyHttpsFromConfig() throws Exception {
+        brooklynProperties.put(BrooklynWebConfig.HTTPS_REQUIRED, true);
+        brooklynProperties.put(BrooklynWebConfig.KEYSTORE_URL, getFile("server.ks"));
+        brooklynProperties.put(BrooklynWebConfig.KEYSTORE_PASSWORD, "password");
+        verifyHttpsFromConfig(brooklynProperties);
+    }
+
+    @Test
+    public void verifyHttpsCiphers() throws Exception {
+        brooklynProperties.put(BrooklynWebConfig.HTTPS_REQUIRED, true);
+        brooklynProperties.put(BrooklynWebConfig.TRANSPORT_PROTOCOLS, "XXX");
+        brooklynProperties.put(BrooklynWebConfig.TRANSPORT_CIPHERS, "XXX");
+        try {
+            verifyHttpsFromConfig(brooklynProperties);
+            fail("Expected to fail due to unsupported ciphers during connection negotiation");
+        } catch (Exception e) {
+            assertTrue(Exceptions.getFirstThrowableOfType(e, SSLPeerUnverifiedException.class) != null ||
+                    Exceptions.getFirstThrowableOfType(e, SSLHandshakeException.class) != null,
+                    "Expected to fail due to inability to negotiate");
+        }
+    }
+
+    private void verifyHttpsFromConfig(BrooklynProperties brooklynProperties) throws Exception {
+        webServer = new BrooklynWebServer(MutableMap.of(), newManagementContext(brooklynProperties));
+        webServer.start();
+        
+        try {
+            KeyStore keyStore = load("client.ks", "password");
+            KeyStore trustStore = load("client.ts", "password");
+            SSLSocketFactory socketFactory = new SSLSocketFactory(SSLSocketFactory.TLS, keyStore, "password", trustStore, (SecureRandom)null, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+            HttpToolResponse response = HttpTool.execAndConsume(
+                    HttpTool.httpClientBuilder()
+                            .port(webServer.getActualPort())
+                            .https(true)
+                            .socketFactory(socketFactory)
+                            .build(),
+                    new HttpGet(webServer.getRootUrl()));
+            assertEquals(response.getResponseCode(), 200);
+        } finally {
+            webServer.stop();
+        }
+    }
+
+    private KeyStore load(String name, String password) throws Exception {
+        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+        FileInputStream instream = new FileInputStream(new File(getFile(name)));
+        keystore.load(instream, password.toCharArray());
+        return keystore;
+    }
+    
+    @Test
+    public void testGetFileFromUrl() throws Exception {
+        // On Windows will treat as relative paths
+        String url = "file:///tmp/special%40file%20with%20spaces";
+        String file = "/tmp/special@file with spaces";
+        assertEquals(getFile(new URL(url)), new File(file).getAbsolutePath());
+    }
+
+    private String getFile(String classpathResource) {
+        // this works because both IDE and Maven run tests with classes/resources on the file system
+        return getFile(getClass().getResource("/" + classpathResource));
+    }
+
+    private String getFile(URL url) {
+        try {
+            return new File(url.toURI()).getAbsolutePath();
+        } catch (URISyntaxException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6f58ef3e/usage/launcher/src/test/java/org/apache/brooklyn/launcher/SimpleYamlLauncherForTests.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/SimpleYamlLauncherForTests.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/SimpleYamlLauncherForTests.java
new file mode 100644
index 0000000..b2dfc5b
--- /dev/null
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/SimpleYamlLauncherForTests.java
@@ -0,0 +1,31 @@
+/*
+ * 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.launcher;
+
+import org.apache.brooklyn.launcher.camp.SimpleYamlLauncher;
+import brooklyn.management.ManagementContext;
+import brooklyn.test.entity.LocalManagementContextForTests;
+
+public class SimpleYamlLauncherForTests extends SimpleYamlLauncher {
+
+    protected ManagementContext newManagementContext() {
+        return new LocalManagementContextForTests();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6f58ef3e/usage/launcher/src/test/java/org/apache/brooklyn/launcher/WebAppRunnerTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/WebAppRunnerTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/WebAppRunnerTest.java
new file mode 100644
index 0000000..3aa6334
--- /dev/null
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/WebAppRunnerTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.launcher;
+
+import org.apache.brooklyn.launcher.BrooklynWebServer;
+import org.apache.brooklyn.launcher.BrooklynLauncher;
+import org.apache.brooklyn.launcher.BrooklynServerDetails;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.test.TestResourceUnavailableException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.entity.basic.Entities;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.management.internal.ManagementContextInternal;
+import brooklyn.test.HttpTestUtils;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.net.Networking;
+
+import com.google.common.collect.Lists;
+
+
+/**
+ * These tests require the brooklyn.war to work. (Should be placed by maven build.)
+ */
+public class WebAppRunnerTest {
+
+    public static final Logger log = LoggerFactory.getLogger(WebAppRunnerTest.class);
+            
+    List<LocalManagementContext> managementContexts = Lists.newCopyOnWriteArrayList();
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        for (LocalManagementContext managementContext : managementContexts) {
+            Entities.destroyAll(managementContext);
+        }
+        managementContexts.clear();
+    }
+    
+    LocalManagementContext newManagementContext(BrooklynProperties brooklynProperties) {
+        LocalManagementContext result = new LocalManagementContext(brooklynProperties);
+        managementContexts.add(result);
+        return result;
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    BrooklynWebServer createWebServer(Map properties) {
+        Map bigProps = MutableMap.copyOf(properties);
+        Map attributes = MutableMap.copyOf( (Map) bigProps.get("attributes") );
+        bigProps.put("attributes", attributes);
+
+        BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty();
+        brooklynProperties.putAll(bigProps);
+        brooklynProperties.put("brooklyn.webconsole.security.provider","brooklyn.rest.security.provider.AnyoneSecurityProvider");
+        brooklynProperties.put("brooklyn.webconsole.security.https.required","false");
+        return new BrooklynWebServer(bigProps, newManagementContext(brooklynProperties));
+    }
+    
+    @Test
+    public void testStartWar1() throws Exception {
+        if (!Networking.isPortAvailable(8090))
+            fail("Another process is using port 8090 which is required for this test.");
+        BrooklynWebServer server = createWebServer(MutableMap.of("port", 8090));
+        assertNotNull(server);
+        
+        try {
+            server.start();
+            assertBrooklynEventuallyAt("http://localhost:8090/");
+        } finally {
+            server.stop();
+        }
+    }
+
+    public static void assertBrooklynEventuallyAt(String url) {
+        HttpTestUtils.assertContentEventuallyContainsText(url, "Brooklyn Web Console");
+    }
+
+    @Test
+    public void testStartSecondaryWar() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war");
+
+        if (!Networking.isPortAvailable(8090))
+            fail("Another process is using port 8090 which is required for this test.");
+        BrooklynWebServer server = createWebServer(
+            MutableMap.of("port", 8090, "war", "brooklyn.war", "wars", MutableMap.of("hello", "hello-world.war")) );
+        assertNotNull(server);
+        
+        try {
+            server.start();
+
+            assertBrooklynEventuallyAt("http://localhost:8090/");
+            HttpTestUtils.assertContentEventuallyContainsText("http://localhost:8090/hello",
+                "This is the home page for a sample application");
+
+        } finally {
+            server.stop();
+        }
+    }
+
+    @Test
+    public void testStartSecondaryWarAfter() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war");
+
+        if (!Networking.isPortAvailable(8090))
+            fail("Another process is using port 8090 which is required for this test.");
+        BrooklynWebServer server = createWebServer(MutableMap.of("port", 8090, "war", "brooklyn.war"));
+        assertNotNull(server);
+        
+        try {
+            server.start();
+            server.deploy("/hello", "hello-world.war");
+
+            assertBrooklynEventuallyAt("http://localhost:8090/");
+            HttpTestUtils.assertContentEventuallyContainsText("http://localhost:8090/hello",
+                "This is the home page for a sample application");
+
+        } finally {
+            server.stop();
+        }
+    }
+
+    @Test
+    public void testStartWithLauncher() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war");
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .brooklynProperties(BrooklynProperties.Factory.newEmpty())
+                .brooklynProperties("brooklyn.webconsole.security.provider","brooklyn.rest.security.provider.AnyoneSecurityProvider")
+                .webapp("/hello", "hello-world.war")
+                .start();
+        BrooklynServerDetails details = launcher.getServerDetails();
+        
+        try {
+            details.getWebServer().deploy("/hello2", "hello-world.war");
+
+            assertBrooklynEventuallyAt(details.getWebServerUrl());
+            HttpTestUtils.assertContentEventuallyContainsText(details.getWebServerUrl()+"hello", "This is the home page for a sample application");
+            HttpTestUtils.assertContentEventuallyContainsText(details.getWebServerUrl()+"hello2", "This is the home page for a sample application");
+            HttpTestUtils.assertHttpStatusCodeEventuallyEquals(details.getWebServerUrl()+"hello0", 404);
+
+        } finally {
+            details.getWebServer().stop();
+            ((ManagementContextInternal)details.getManagementContext()).terminate();
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6f58ef3e/usage/launcher/src/test/java/org/apache/brooklyn/launcher/YamlLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/YamlLauncher.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/YamlLauncher.java
new file mode 100644
index 0000000..f383895
--- /dev/null
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/YamlLauncher.java
@@ -0,0 +1,35 @@
+/*
+ * 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.launcher;
+
+import org.apache.brooklyn.launcher.camp.SimpleYamlLauncher;
+
+public class YamlLauncher {
+
+    public static void main(String[] args) {
+        SimpleYamlLauncher l = new SimpleYamlLauncher();
+        l.setShutdownAppsOnExit(true);
+        
+        l.launchAppYaml("java-web-app-and-db-with-function.yaml");
+//        l.launchAppYaml("java-web-app-and-memsql.yaml");
+//        l.launchAppYaml("memsql.yaml");
+//        l.launchAppYaml("classpath://mongo-blueprint.yaml");
+    }
+
+}