You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/07/16 01:09:45 UTC

[01/12] incubator-brooklyn git commit: brooklyn-qa: add org.apache package prefix

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 726bebca3 -> 12625d82c


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/test/java/org/apache/brooklyn/qa/load/LoadTest.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/test/java/org/apache/brooklyn/qa/load/LoadTest.java b/usage/qa/src/test/java/org/apache/brooklyn/qa/load/LoadTest.java
new file mode 100644
index 0000000..d6c479c
--- /dev/null
+++ b/usage/qa/src/test/java/org/apache/brooklyn/qa/load/LoadTest.java
@@ -0,0 +1,244 @@
+/*
+ * 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.qa.load;
+
+import org.apache.brooklyn.qa.load.SimulatedTheeTierApp;
+import static org.testng.Assert.assertEquals;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+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.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.entity.trait.Startable;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.location.Location;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.ha.HighAvailabilityMode;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.test.PerformanceTestUtils;
+import brooklyn.util.os.Os;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+/**
+ * Customers ask about the scalability of Brooklyn. These load tests investigate how many 
+ * concurrent apps can be deployed and managed by a single Brooklyn management node.
+ * 
+ * The apps are "simulated" in that they don't create the underlying resources 
+ * (we are not checking if the test machine can run 100s of app-servers simultaneously!) 
+ * The install/customize/launch will instead execute ssh commands of comparable length,
+ * but that just echo rather than execute the actual commands.
+ * 
+ * "SIMULATE_EXTERNAL_MONITORING" means that we do not poll the entities directly (over ssh, http or 
+ * whatever). Instead we simulate the metrics being injected directly to be set on the entity (e.g. 
+ * having been collected from a Graphite server).
+ * 
+ * "SKIP_SSH_ON_START" means don't do the normal install+customize+launch ssh commands. Instead, just
+ * startup the entities so we can monitor their resource usage.
+ */
+public class LoadTest {
+
+    // TODO Could/should issue provisioning request through REST api, rather than programmatically; 
+    // and poll to detect completion.
+    
+    /*
+     * Useful commands when investigating:
+     *     LOG_FILE=usage/qa/brooklyn-camp-tests.log
+     *     grep -E "OutOfMemoryError|[P|p]rovisioning time|sleeping before|CPU fraction|LoadTest using" $LOG_FILE | less
+     *     grep -E "OutOfMemoryError|[P|p]rovisioning time" $LOG_FILE; grep "CPU fraction" $LOG_FILE | tail -1; grep "LoadTest using" $LOG_FILE | tail -1
+     *     grep -E "OutOfMemoryError|LoadTest using" $LOG_FILE
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(LoadTest.class);
+
+    private File persistenceDir;
+    private BrooklynLauncher launcher;
+    private String webServerUrl;
+    private ManagementContext managementContext;
+    private ListeningExecutorService executor;
+    private Future<?> cpuFuture;
+    
+    private Location localhost;
+    
+    List<Duration> provisioningTimes;
+
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        // Create management node
+        persistenceDir = Files.createTempDir();
+        launcher = BrooklynLauncher.newInstance()
+                .persistMode(PersistMode.CLEAN)
+                .highAvailabilityMode(HighAvailabilityMode.MASTER)
+                .persistenceDir(persistenceDir)
+                .start();
+        webServerUrl = launcher.getServerDetails().getWebServerUrl();
+        managementContext = launcher.getServerDetails().getManagementContext();
+
+        localhost = managementContext.getLocationRegistry().resolve("localhost");
+        
+        provisioningTimes = Collections.synchronizedList(Lists.<Duration>newArrayList());
+
+        // Create executors
+        executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
+
+        // Monitor utilisation (memory/CPU) while tests run
+        executor.submit(new Callable<Void>() {
+            public Void call() {
+                try {
+                    while (true) {
+                        managementContext.getExecutionManager(); // force GC to be instantiated
+                        String usage = ((LocalManagementContext)managementContext).getGarbageCollector().getUsageString();
+                        LOG.info("LoadTest using "+usage);
+                        Thread.sleep(1000);
+                    }
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt(); // exit gracefully
+                } catch (Exception e) {
+                    LOG.error("Error getting usage info", e);
+                }
+                return null;
+            }});
+        
+        cpuFuture = PerformanceTestUtils.sampleProcessCpuTime(Duration.ONE_SECOND, "during testProvisionAppsConcurrently");
+
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (cpuFuture != null) cpuFuture.cancel(true);
+        if (executor != null) executor.shutdownNow();
+        if (launcher != null) launcher.terminate();
+        if (persistenceDir != null) Os.deleteRecursively(persistenceDir);
+    }
+    
+    /**
+     * Creates multiple apps simultaneously. 
+     * 
+     * Long-term target is 50 concurrent provisioning requests (which may be issued while there are
+     * many existing applications under management). Until we reach that point, we can partition the
+     * load across multiple (separate) brooklyn management nodes.
+     *   TODO TBD: is that 50 VMs worth, or 50 apps with 4 VMs in each? 
+     * 
+     * TODO Does not measure the cost of jclouds for creating all the VMs/containers.
+     */
+    @Test(groups="Acceptance")
+    public void testLocalhostProvisioningAppsConcurrently() throws Exception {
+        final int NUM_CONCURRENT_APPS_PROVISIONING = 20; 
+        
+        List<ListenableFuture<StartableApplication>> futures = Lists.newArrayList();
+        for (int i = 0; i < NUM_CONCURRENT_APPS_PROVISIONING; i++) {
+            ListenableFuture<StartableApplication> future = executor.submit(newProvisionAppTask(managementContext, 
+                    EntitySpec.create(StartableApplication.class, SimulatedTheeTierApp.class)
+                            .configure(SimulatedTheeTierApp.SIMULATE_EXTERNAL_MONITORING, true)
+                            .displayName("Simulated app "+i)));
+            futures.add(future);
+        }
+        
+        List<StartableApplication> apps = Futures.allAsList(futures).get();
+        
+        for (StartableApplication app : apps) {
+            assertEquals(app.getAttribute(Startable.SERVICE_UP), (Boolean)true);
+        }
+    }
+    
+    /**
+     * Creates many apps, to monitor resource usage etc.
+     * 
+     * "SIMULATE_EXTERNAL_MONITORING" means that we do not poll the entities directly (over ssh, http or 
+     * whatever). Instead we simulate the metrics being injected directly to be set on the entity (e.g. 
+     * having been collected from a Graphite server).
+     * 
+     * Long-term target is 2500 VMs under management.
+     * Until we reach that point, we can partition the load across multiple (separate) brooklyn management nodes.
+     */
+    @Test(groups="Acceptance")
+    public void testLocalhostManyApps() throws Exception {
+        final int NUM_APPS = 630; // target is 2500 VMs; each blueprint has 4 (rounding up)
+        final int NUM_APPS_PER_BATCH = 10;
+        final int SLEEP_BETWEEN_BATCHES = 10*1000;
+        final boolean SKIP_SSH_ON_START = true; // getting ssh errors otherwise!
+        
+        int counter = 0;
+        
+        for (int i = 0; i < NUM_APPS / NUM_APPS_PER_BATCH; i++) {
+            List<ListenableFuture<StartableApplication>> futures = Lists.newArrayList();
+            for (int j = 0; j < NUM_APPS_PER_BATCH; j++) {
+                ListenableFuture<StartableApplication> future = executor.submit(newProvisionAppTask(
+                        managementContext, 
+                        EntitySpec.create(StartableApplication.class, SimulatedTheeTierApp.class)
+                                .configure(SimulatedTheeTierApp.SIMULATE_EXTERNAL_MONITORING, true)
+                                .configure(SimulatedTheeTierApp.SKIP_SSH_ON_START, SKIP_SSH_ON_START)
+                                .displayName("Simulated app "+(++counter))));
+                futures.add(future);
+            }
+            
+            List<StartableApplication> apps = Futures.allAsList(futures).get();
+            
+            for (StartableApplication app : apps) {
+                assertEquals(app.getAttribute(Startable.SERVICE_UP), (Boolean)true);
+            }
+
+            synchronized (provisioningTimes) {
+                LOG.info("cycle="+i+"; numApps="+counter+": provisioning times: "+provisioningTimes);
+                provisioningTimes.clear();
+            }
+
+            LOG.info("cycle="+i+"; numApps="+counter+": sleeping before next batch of apps");
+            Thread.sleep(SLEEP_BETWEEN_BATCHES);
+        }
+    }
+    
+    protected <T extends StartableApplication> Callable<T> newProvisionAppTask(final ManagementContext managementContext, final EntitySpec<T> entitySpec) {
+        return new Callable<T>() {
+            public T call() {
+                Stopwatch stopwatch = Stopwatch.createStarted();
+                T app = managementContext.getEntityManager().createEntity(entitySpec);
+                Entities.startManagement(app, managementContext);
+                app.start(ImmutableList.of(localhost));
+                Duration duration = Duration.of(stopwatch.elapsed(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
+                LOG.info("Provisioning time: "+duration);
+                provisioningTimes.add(duration);
+
+                return app;
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/MonitorUtilsTest.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/MonitorUtilsTest.java b/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/MonitorUtilsTest.java
new file mode 100644
index 0000000..cf377e9
--- /dev/null
+++ b/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/MonitorUtilsTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.qa.longevity;
+
+import org.apache.brooklyn.qa.longevity.MonitorUtils;
+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 java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+import org.apache.brooklyn.qa.longevity.MonitorUtils.ProcessHasStderr;
+import brooklyn.util.os.Os;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+
+public class MonitorUtilsTest {
+
+    @Test(enabled=false, timeOut=1000) // Demonstrates that process.waitFor() hangs for big output streams
+    public void testExecuteAndWaitFor() throws Exception {
+        Process process = createDumpingProcess(false);
+        process.waitFor();
+        fail("Should block while waiting to consume process output");
+    }
+
+    @Test(enabled=false, timeOut=1000) // Demonstrates that process.waitFor() hangs for big err streams
+    public void testExecuteAndWaitForErr() throws Exception {
+        Process process = createDumpingProcess(true);
+        process.waitFor();
+        fail("Should block while waiting to consume process output");
+    }
+
+    @Test(timeOut=1000)
+    public void testExecuteAndWaitForConsumingOutputStream() throws Exception {
+        Process process = createDumpingProcess(false);
+        String out = MonitorUtils.waitFor(process);
+        assertTrue(out.length() > 100000, "out.size="+out.length());
+    }
+
+    @Test(timeOut=1000, expectedExceptions=IllegalStateException.class)
+    public void testExecuteAndWaitForConsumingErrorStream() throws Exception {
+        Process process = createDumpingProcess(true);
+        MonitorUtils.waitFor(process);
+    }
+
+    private Process createDumpingProcess(boolean writeToErr) throws IOException {
+        String errSuffix = writeToErr ? " >&2" : "";
+        //Windows limits the length of the arguments so echo multiple times instead
+        String bigstr = Strings.repeat("a", 8000);
+        String bigcmd = Strings.repeat(getSilentPrefix() + "echo " + bigstr + errSuffix + Os.LINE_SEPARATOR, 15);
+        File file = Os.newTempFile("test-consume", ".bat");
+        file.setExecutable(true);
+        Files.write(bigcmd, file, Charsets.UTF_8);
+        Process process = MonitorUtils.exec(file.getAbsolutePath());
+        return process;
+    }
+
+    @Test(groups="UNIX")
+    public void testFindOwnPid() throws Exception {
+        int ownpid = MonitorUtils.findOwnPid();
+        assertTrue(ownpid > 0, "ownpid=$ownpid");
+        assertTrue(MonitorUtils.isPidRunning(ownpid, "java"),"java is not running");
+    }
+
+    @Test(groups="UNIX")
+    public void testIsPidRunning() throws Exception {
+        int usedPid = MonitorUtils.findOwnPid();
+
+        //the child process will terminate freeing it PID
+        String[] cmd = new String[]{"bash", "-c", "echo $$"};
+        Process process = Runtime.getRuntime().exec(cmd);
+        String out = MonitorUtils.waitFor(process);
+        int unusedPid = Integer.parseInt(out.trim());
+
+        assertTrue(MonitorUtils.isPidRunning(usedPid));
+        assertFalse(MonitorUtils.isPidRunning(unusedPid));
+        
+        try {
+            assertFalse(MonitorUtils.isPidRunning(1234567)); // too large
+        } catch (ProcessHasStderr e) {
+            // expected on osx
+        }
+    }
+
+    @Test(groups="UNIX")
+    public void testGetRunningPids() throws Exception {
+        int ownpid = MonitorUtils.findOwnPid();
+
+        List<Integer> javapids = MonitorUtils.getRunningPids("java");
+
+        assertTrue(javapids.contains(ownpid), "javapids="+javapids+"; ownpid="+ownpid);
+    }
+
+    @Test
+    public void testIsUrlUp() throws Exception {
+        assertFalse(MonitorUtils.isUrlUp(new URL("http://localhost/thispathdoesnotexist")));
+    }
+
+    @Test(groups="UNIX")
+    public void testSearchLog() throws Exception {
+        String fileContents = "line1\nline2\nline3\n";
+        File file = File.createTempFile("monitorUtilsTest.testSearchLog", ".txt");
+        Files.write(fileContents, file, Charsets.UTF_8);
+
+        try {
+            assertEquals(MonitorUtils.searchLog(file, "line1"), Arrays.asList("line1"));
+            assertEquals(MonitorUtils.searchLog(file, "line1|line2"), Arrays.asList("line1", "line2"));
+            assertEquals(MonitorUtils.searchLog(file, "textnotthere"), Collections.emptyList());
+            assertEquals(MonitorUtils.searchLog(file, "line"), Arrays.asList("line1", "line2", "line3"));
+        } finally {
+            file.delete();
+        }
+    }
+
+    @Test(groups="Integration")
+    public void testMemoryUsage() throws Exception {
+        int ownpid = MonitorUtils.findOwnPid();
+
+        MonitorUtils.MemoryUsage memUsage = MonitorUtils.getMemoryUsage(ownpid);
+        assertTrue(memUsage.totalInstances > 0, memUsage.toString());
+        assertTrue(memUsage.totalMemoryBytes > 0, memUsage.toString());
+        assertEquals(memUsage.getInstanceCounts(), Collections.emptyMap());
+
+        MonitorUtils.MemoryUsage memUsage2 = MonitorUtils.getMemoryUsage(ownpid, MonitorUtilsTest.class.getCanonicalName(),0);
+        assertEquals(memUsage2.getInstanceCounts(), ImmutableMap.of(MonitorUtilsTest.class.getCanonicalName(), 1));
+
+        MonitorUtils.MemoryUsage memUsage3 = MonitorUtils.getMemoryUsage(ownpid, MonitorUtilsTest.class.getCanonicalName(), 2);
+        assertEquals(memUsage3.getInstanceCounts(), Collections.emptyMap());
+    }
+
+    private String getSilentPrefix() {
+        if (Os.isMicrosoftWindows()) {
+            return "@";
+        } else {
+            return "";
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/webcluster/SinusoidalLoadGenerator.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/webcluster/SinusoidalLoadGenerator.java b/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/webcluster/SinusoidalLoadGenerator.java
new file mode 100644
index 0000000..17e7a32
--- /dev/null
+++ b/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/webcluster/SinusoidalLoadGenerator.java
@@ -0,0 +1,90 @@
+/*
+ * 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.qa.longevity.webcluster;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.enricher.basic.AbstractEnricher;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.event.AttributeSensor;
+
+import com.google.common.base.Throwables;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * Periodically publishes values in the range of 0 to #amplitude. 
+ * The value varies sinusoidally over time.
+ */
+public class SinusoidalLoadGenerator extends AbstractEnricher {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SinusoidalLoadGenerator.class);
+
+    public static final ConfigKey<AttributeSensor<Double>> TARGET = ConfigKeys.newConfigKey(new TypeToken<AttributeSensor<Double>>() {}, "target");
+    
+    public static final ConfigKey<Long> PUBLISH_PERIOD_MS = ConfigKeys.newLongConfigKey("publishPeriodMs");
+
+    public static final ConfigKey<Long> SIN_PERIOD_MS = ConfigKeys.newLongConfigKey("sinPeriodMs");
+
+    public static final ConfigKey<Double> SIN_AMPLITUDE = ConfigKeys.newDoubleConfigKey("sinAmplitude");
+
+    private final ScheduledExecutorService executor;
+    
+    public SinusoidalLoadGenerator() {
+        this.executor = Executors.newSingleThreadScheduledExecutor();
+    }
+    
+    public SinusoidalLoadGenerator(AttributeSensor<Double> target, long publishPeriodMs, long sinPeriodMs, double sinAmplitude) {
+        config().set(TARGET, target);
+        config().set(PUBLISH_PERIOD_MS, publishPeriodMs);
+        config().set(SIN_PERIOD_MS, sinPeriodMs);
+        config().set(SIN_AMPLITUDE, sinAmplitude);
+        this.executor = Executors.newSingleThreadScheduledExecutor();
+    }
+    
+    @Override
+    public void setEntity(final EntityLocal entity) {
+        super.setEntity(entity);
+        
+        executor.scheduleAtFixedRate(new Runnable() {
+            @Override public void run() {
+                try {
+                    long time = System.currentTimeMillis();
+                    double val = getRequiredConfig(SIN_AMPLITUDE) * (1 + Math.sin( (1.0*time) / getRequiredConfig(SIN_PERIOD_MS) * Math.PI * 2  - Math.PI/2 )) / 2;
+                    entity.setAttribute(getRequiredConfig(TARGET), val);
+                } catch (Throwable t) {
+                    LOG.warn("Error generating sinusoidal-load metric", t);
+                    throw Throwables.propagate(t);
+                }
+            }
+
+        }, 0, getRequiredConfig(PUBLISH_PERIOD_MS), TimeUnit.MILLISECONDS);
+    }
+    
+    @Override
+    public void destroy() {
+        executor.shutdownNow();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/webcluster/WebClusterApp.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/webcluster/WebClusterApp.java b/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/webcluster/WebClusterApp.java
new file mode 100644
index 0000000..0d3d694
--- /dev/null
+++ b/usage/qa/src/test/java/org/apache/brooklyn/qa/longevity/webcluster/WebClusterApp.java
@@ -0,0 +1,101 @@
+/*
+ * 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.qa.longevity.webcluster;
+
+import java.util.List;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.enricher.Enrichers;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.proxy.nginx.NginxController;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
+import brooklyn.entity.webapp.jboss.JBoss7Server;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.basic.Sensors;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.policy.EnricherSpec;
+import brooklyn.policy.autoscaling.AutoScalerPolicy;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.collect.Lists;
+
+public class WebClusterApp extends AbstractApplication {
+
+    static BrooklynProperties config = BrooklynProperties.Factory.newDefault();
+
+    public static final String WAR_PATH = "classpath://hello-world.war";
+
+    private static final long loadCyclePeriodMs = 2 * 60 * 1000L;
+
+    @Override
+    public void initApp() {
+        final AttributeSensor<Double> sinusoidalLoad =
+                Sensors.newDoubleSensor("brooklyn.qa.sinusoidalLoad", "Sinusoidal server load");
+        AttributeSensor<Double> averageLoad =
+                Sensors.newDoubleSensor("brooklyn.qa.averageLoad", "Average load in cluster");
+
+        NginxController nginxController = addChild(EntitySpec.create(NginxController.class)
+                // .configure("domain", "webclusterexample.brooklyn.local")
+                .configure("port", "8000+"));
+
+        EntitySpec<JBoss7Server> jbossSpec = EntitySpec.create(JBoss7Server.class)
+                .configure("httpPort", "8080+")
+                .configure("war", WAR_PATH)
+                .enricher(EnricherSpec.create(SinusoidalLoadGenerator.class)
+                        .configure(SinusoidalLoadGenerator.TARGET, sinusoidalLoad)
+                        .configure(SinusoidalLoadGenerator.PUBLISH_PERIOD_MS, 500L)
+                        .configure(SinusoidalLoadGenerator.SIN_PERIOD_MS, loadCyclePeriodMs)
+                        .configure(SinusoidalLoadGenerator.SIN_AMPLITUDE, 1d));
+
+        ControlledDynamicWebAppCluster web = addChild(EntitySpec.create(ControlledDynamicWebAppCluster.class)
+                .displayName("WebApp cluster")
+                .configure("controller", nginxController)
+                .configure("initialSize", 1)
+                .configure("memberSpec", jbossSpec));
+
+        web.getCluster().addEnricher(Enrichers.builder()
+                .aggregating(sinusoidalLoad)
+                .publishing(averageLoad)
+                .fromMembers()
+                .computingAverage()
+                .build());
+        web.getCluster().addPolicy(AutoScalerPolicy.builder()
+                .metric(averageLoad)
+                .sizeRange(1, 3)
+                .metricRange(0.3, 0.7)
+                .build());
+    }
+    
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", "localhost");
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .application(EntitySpec.create(StartableApplication.class, WebClusterApp.class).displayName("Brooklyn WebApp Cluster example"))
+                .webconsolePort(port)
+                .location(location)
+                .start();
+         
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}


[02/12] incubator-brooklyn git commit: brooklyn-qa: add org.apache package prefix

Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedMySqlNodeImpl.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedMySqlNodeImpl.java b/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedMySqlNodeImpl.java
new file mode 100644
index 0000000..8a24f73
--- /dev/null
+++ b/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedMySqlNodeImpl.java
@@ -0,0 +1,183 @@
+/*
+ * 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.qa.load;
+
+import static java.lang.String.format;
+
+import java.util.concurrent.Callable;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver;
+import brooklyn.entity.database.mysql.MySqlNode;
+import brooklyn.entity.database.mysql.MySqlNodeImpl;
+import brooklyn.entity.database.mysql.MySqlSshDriver;
+import brooklyn.entity.software.SshEffectorTasks;
+import brooklyn.event.feed.function.FunctionFeed;
+import brooklyn.event.feed.function.FunctionPollConfig;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.task.DynamicTasks;
+import brooklyn.util.task.system.ProcessTaskWrapper;
+import brooklyn.util.time.CountdownTimer;
+import brooklyn.util.time.Duration;
+
+/**
+ * @see SimulatedJBoss7ServerImpl for description of purpose and configuration options.
+ */
+public class SimulatedMySqlNodeImpl extends MySqlNodeImpl {
+
+    public static final ConfigKey<Boolean> SIMULATE_ENTITY = SimulatedTheeTierApp.SIMULATE_ENTITY;
+    public static final ConfigKey<Boolean> SIMULATE_EXTERNAL_MONITORING = SimulatedTheeTierApp.SIMULATE_EXTERNAL_MONITORING;
+    public static final ConfigKey<Boolean> SKIP_SSH_ON_START = SimulatedTheeTierApp.SKIP_SSH_ON_START;
+    
+    private FunctionFeed feed;
+    
+    @Override
+    public Class<?> getDriverInterface() {
+        return SimulatedMySqlSshDriver.class;
+    }
+
+    @Override
+    protected void connectSensors() {
+        boolean simulateExternalMonitoring = getConfig(SIMULATE_EXTERNAL_MONITORING);
+        if (simulateExternalMonitoring) {
+            setAttribute(DATASTORE_URL, String.format("mysql://%s:%s/", getAttribute(HOSTNAME), getAttribute(MYSQL_PORT)));
+            
+            feed = FunctionFeed.builder()
+                    .entity(this)
+                    .period(Duration.FIVE_SECONDS)
+                    .poll(new FunctionPollConfig<Boolean, Boolean>(SERVICE_UP)
+                            .callable(new Callable<Boolean>() {
+                                private int counter = 0;
+                                public Boolean call() {
+                                    setAttribute(QUERIES_PER_SECOND_FROM_MYSQL, (double)(counter++ % 100));
+                                    return true;
+                                }})
+                            .setOnFailureOrException(false))
+                    .build();
+        } else {
+            super.connectSensors();
+        }
+    }
+
+    public static class SimulatedMySqlSshDriver extends MySqlSshDriver {
+
+        private int counter = 0;
+        
+        public SimulatedMySqlSshDriver(SimulatedMySqlNodeImpl entity, SshMachineLocation machine) {
+            super(entity, machine);
+        }
+        
+        // simulate metrics, for if using ssh polling
+        @Override
+        public String getStatusCmd() {
+            if (entity.getConfig(SIMULATE_ENTITY)) {
+                return "echo Uptime: 2427  Threads: 1  Questions: 581  Slow queries: 0  Opens: 53  Flush tables: 1  Open tables: 35  Queries per second avg: "+(counter++ % 100);
+            } else {
+                return super.getStatusCmd();
+            }
+        }
+
+        @Override
+        public void install() {
+            if (entity.getConfig(SKIP_SSH_ON_START)) {
+                // no-op
+            } else {
+                super.install();
+            }
+        }
+        
+        // Not applying creation-script etc, as that requires launching msyqld (so would not scale for single-machine testing)
+        // This is a copy of super.customize, but with the mysqladmin-exec disabled
+        @Override
+        public void customize() {
+            if (!entity.getConfig(SIMULATE_ENTITY)) {
+                super.customize();
+                return;
+            } else if (entity.getConfig(SKIP_SSH_ON_START)) {
+                // no-op
+            } else {
+                copyDatabaseConfigScript();
+    
+                newScript(CUSTOMIZING)
+                    .updateTaskAndFailOnNonZeroResultCode()
+                    .body.append(
+                        "chmod 600 "+getConfigFile(),
+                        getBaseDir()+"/scripts/mysql_install_db "+
+                            "--basedir="+getBaseDir()+" --datadir="+getDataDir()+" "+
+                            "--defaults-file="+getConfigFile())
+                    .execute();
+    
+                // launch, then we will configure it
+                launch();
+    
+                CountdownTimer timer = Duration.seconds(20).countdownTimer();
+                boolean hasCreationScript = copyDatabaseCreationScript();
+                timer.waitForExpiryUnchecked();
+    
+                // DELIBERATELY SKIPPED FOR SCALABILITY TESTING ON SINGLE MACHINE
+                DynamicTasks.queue(
+                    SshEffectorTasks.ssh(
+                        "cd "+getRunDir(),
+                        "echo skipping exec of "+getBaseDir()+"/bin/mysqladmin --defaults-file="+getConfigFile()+" --password= password "+getPassword()
+                    ).summary("setting password"));
+    
+                if (hasCreationScript)
+                    executeScriptFromInstalledFileAsync("creation-script.sql");
+    
+                // not sure necessary to stop then subsequently launch, but seems safest
+                // (if skipping, use a flag in launch to indicate we've just launched it)
+                stop();
+            }
+        }
+
+        @Override
+        public void launch() {
+            if (!entity.getConfig(SIMULATE_ENTITY)) {
+                super.launch();
+                return;
+            }
+            
+            entity.setAttribute(MySqlNode.PID_FILE, getRunDir() + "/" + AbstractSoftwareProcessSshDriver.PID_FILENAME);
+            
+            if (entity.getConfig(SKIP_SSH_ON_START)) {
+                // minimal ssh, so that isRunning will subsequently work
+                newScript(MutableMap.of("usePidFile", true), LAUNCHING)
+                        .body.append(
+                                format("nohup sleep 100000 > %s 2>&1 < /dev/null &", getLogFile()))
+                        .execute();
+            } else {
+                newScript(MutableMap.of("usePidFile", true), LAUNCHING)
+                    .updateTaskAndFailOnNonZeroResultCode()
+                    .body.append(format("echo skipping normal exec of nohup %s/bin/mysqld --defaults-file=%s --user=`whoami` > %s 2>&1 < /dev/null &", getBaseDir(), getConfigFile(), getLogFile()))
+                    .body.append(format("nohup sleep 100000 > %s 2>&1 < /dev/null &", getLogFile()))
+                    .execute();
+            }
+        }
+
+        @Override
+        public ProcessTaskWrapper<Integer> executeScriptFromInstalledFileAsync(String filenameAlreadyInstalledAtServer) {
+            return DynamicTasks.queue(
+                    SshEffectorTasks.ssh(
+                                    "cd "+getRunDir(),
+                                    "echo skipping exec of "+getBaseDir()+"/bin/mysql --defaults-file="+getConfigFile()+" < "+filenameAlreadyInstalledAtServer)
+                            .summary("executing datastore script "+filenameAlreadyInstalledAtServer));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedNginxControllerImpl.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedNginxControllerImpl.java b/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedNginxControllerImpl.java
new file mode 100644
index 0000000..8813a4f
--- /dev/null
+++ b/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedNginxControllerImpl.java
@@ -0,0 +1,196 @@
+/*
+ * 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.qa.load;
+
+import static java.lang.String.format;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.concurrent.Callable;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Group;
+import brooklyn.entity.proxy.nginx.NginxControllerImpl;
+import brooklyn.entity.proxy.nginx.NginxSshDriver;
+import brooklyn.entity.proxy.nginx.UrlMapping;
+import brooklyn.event.SensorEvent;
+import brooklyn.event.SensorEventListener;
+import brooklyn.event.feed.ConfigToAttributes;
+import brooklyn.event.feed.function.FunctionFeed;
+import brooklyn.event.feed.function.FunctionPollConfig;
+import brooklyn.event.feed.http.HttpFeed;
+import brooklyn.event.feed.http.HttpPollConfig;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.policy.PolicySpec;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.net.Networking;
+
+import com.google.common.base.Functions;
+
+/**
+ * @see SimulatedJBoss7ServerImpl for description of purpose and configuration options.
+ */
+public class SimulatedNginxControllerImpl extends NginxControllerImpl {
+
+    public static final ConfigKey<Boolean> SIMULATE_ENTITY = SimulatedTheeTierApp.SIMULATE_ENTITY;
+    public static final ConfigKey<Boolean> SIMULATE_EXTERNAL_MONITORING = SimulatedTheeTierApp.SIMULATE_EXTERNAL_MONITORING;
+    public static final ConfigKey<Boolean> SKIP_SSH_ON_START = SimulatedTheeTierApp.SKIP_SSH_ON_START;
+    
+    private HttpFeed httpFeed;
+    private FunctionFeed functionFeed;
+    
+    @Override
+    public Class<?> getDriverInterface() {
+        return SimulatedNginxSshDriver.class;
+    }
+
+    @Override
+    public void connectSensors() {
+        boolean simulateEntity = getConfig(SIMULATE_ENTITY);
+        boolean simulateExternalMonitoring = getConfig(SIMULATE_EXTERNAL_MONITORING);
+
+        if (!simulateEntity && !simulateExternalMonitoring) {
+            super.connectSensors();
+            return;
+        }
+
+        // From AbstractController.connectSensors
+        if (getUrl()==null) {
+            setAttribute(MAIN_URI, URI.create(inferUrl()));
+            setAttribute(ROOT_URL, inferUrl());
+        }
+        addServerPoolMemberTrackingPolicy();
+
+        // From NginxController.connectSensors
+        ConfigToAttributes.apply(this);
+
+        if (!simulateExternalMonitoring) {
+            // if simulating entity, then simulate work of periodic HTTP request; TODO but not parsing JSON response
+            String uriToPoll = (simulateEntity) ? "http://localhost:8081" : getAttribute(MAIN_URI).toString();
+            
+            httpFeed = HttpFeed.builder()
+                    .entity(this)
+                    .period(getConfig(HTTP_POLL_PERIOD))
+                    .baseUri(uriToPoll)
+                    .poll(new HttpPollConfig<Boolean>(SERVICE_UP)
+                            .onSuccess(Functions.constant(true))
+                            .onFailureOrException(Functions.constant(true)))
+                    .build();
+        }
+        
+        functionFeed = FunctionFeed.builder()
+                .entity(this)
+                .period(getConfig(HTTP_POLL_PERIOD))
+                .poll(new FunctionPollConfig<Boolean,Boolean>(SERVICE_UP)
+                        .callable(new Callable<Boolean>() {
+                            public Boolean call() {
+                                return true;
+                            }}))
+                .build();
+
+        // Can guarantee that parent/managementContext has been set
+        Group urlMappings = getConfig(URL_MAPPINGS);
+        if (urlMappings != null) {
+            // Listen to the targets of each url-mapping changing
+            subscribeToMembers(urlMappings, UrlMapping.TARGET_ADDRESSES, new SensorEventListener<Collection<String>>() {
+                    @Override public void onEvent(SensorEvent<Collection<String>> event) {
+                        updateNeeded();
+                    }
+                });
+
+            // Listen to url-mappings being added and removed
+            urlMappingsMemberTrackerPolicy = addPolicy(PolicySpec.create(UrlMappingsMemberTrackerPolicy.class)
+                    .configure("group", urlMappings));
+        }
+    }
+
+    @Override
+    protected void disconnectSensors() {
+        super.disconnectSensors();
+        if (httpFeed != null) httpFeed.stop();
+        if (functionFeed != null) functionFeed.stop();
+    }
+
+    public static class SimulatedNginxSshDriver extends NginxSshDriver {
+        public SimulatedNginxSshDriver(SimulatedNginxControllerImpl entity, SshMachineLocation machine) {
+            super(entity, machine);
+        }
+
+        @Override
+        public void install() {
+            if (entity.getConfig(SKIP_SSH_ON_START)) {
+                // no-op
+            } else {
+                super.install();
+            }
+        }
+        
+        @Override
+        public void customize() {
+            if (entity.getConfig(SKIP_SSH_ON_START)) {
+                // no-op
+            } else {
+                super.customize();
+            }
+        }
+        
+        @Override
+        public void launch() {
+            if (!entity.getConfig(SIMULATE_ENTITY)) {
+                super.launch();
+                return;
+            }
+            
+            Networking.checkPortsValid(MutableMap.of("httpPort", getPort()));
+
+            if (entity.getConfig(SKIP_SSH_ON_START)) {
+                // minimal ssh, so that isRunning will subsequently work
+                newScript(MutableMap.of("usePidFile", getPidFile()), LAUNCHING)
+                        .body.append(
+                                format("mkdir -p %s/logs", getRunDir()),
+                                format("nohup sleep 100000 > %s 2>&1 < /dev/null &", getLogFileLocation()))
+                        .execute();
+            } else {
+                newScript(MutableMap.of("usePidFile", false), LAUNCHING)
+                        .body.append(
+                                format("cd %s", getRunDir()),
+                                "echo skipping exec of requireExecutable ./sbin/nginx",
+                                sudoBashCIfPrivilegedPort(getPort(), format(
+                                        "echo skipping exec of nohup ./sbin/nginx -p %s/ -c conf/server.conf > %s 2>&1 &", getRunDir(), getLogFileLocation())),
+                                format("nohup sleep 100000 > %s 2>&1 < /dev/null &", getLogFileLocation()),
+                                format("echo $! > "+getPidFile()),
+                                format("for i in {1..10}\n" +
+                                        "do\n" +
+                                        "    test -f %1$s && ps -p `cat %1$s` && exit\n" +
+                                        "    sleep 1\n" +
+                                        "done\n" +
+                                        "echo \"No explicit error launching nginx but couldn't find process by pid; continuing but may subsequently fail\"\n" +
+                                        "cat %2$s | tee /dev/stderr",
+                                        getPidFile(), getLogFileLocation()))
+                        .execute();
+            }
+        }
+
+        // Use pid file, because just simulating the run of nginx
+        @Override
+        public void stop() {
+            newScript(MutableMap.of("usePidFile", getPidFile()), STOPPING).execute();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedTheeTierApp.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedTheeTierApp.java b/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedTheeTierApp.java
new file mode 100644
index 0000000..273d130
--- /dev/null
+++ b/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedTheeTierApp.java
@@ -0,0 +1,140 @@
+/*
+ * 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.qa.load;
+
+import static brooklyn.event.basic.DependentConfiguration.attributeWhenReady;
+import static brooklyn.event.basic.DependentConfiguration.formatString;
+
+import java.util.Collection;
+import java.util.List;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.enricher.Enrichers;
+import brooklyn.enricher.HttpLatencyDetector;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.database.mysql.MySqlNode;
+import brooklyn.entity.group.DynamicCluster;
+import brooklyn.entity.java.JavaEntityMethods;
+import brooklyn.entity.proxy.nginx.NginxController;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.trait.Startable;
+import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
+import brooklyn.entity.webapp.DynamicWebAppCluster;
+import brooklyn.entity.webapp.JavaWebAppService;
+import brooklyn.entity.webapp.WebAppService;
+import brooklyn.entity.webapp.WebAppServiceConstants;
+import brooklyn.entity.webapp.jboss.JBoss7Server;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.location.basic.PortRanges;
+import brooklyn.policy.autoscaling.AutoScalerPolicy;
+import brooklyn.util.CommandLineUtil;
+import brooklyn.util.collections.MutableSet;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+/**
+ * A 3-tier app where all components are just "simulated" - they don't actually run 
+ * real app-servers or databases, instead just executing a "sleep" command to simulate 
+ * the running process.
+ * 
+ * This is useful for load testing, where we want to test the performance of Brooklyn
+ * rather than the ability to host many running app-servers.
+ * 
+ * The app is based on WebClusterDatabaseExampleApp
+ * 
+ * @see SimulatedJBoss7ServerImpl for description of purpose and configuration options.
+ */
+public class SimulatedTheeTierApp extends AbstractApplication {
+
+    public static final ConfigKey<Boolean> SIMULATE_ENTITY = ConfigKeys.newBooleanConfigKey("simulateEntity", "", true);
+    
+    public static final ConfigKey<Boolean> SIMULATE_EXTERNAL_MONITORING = ConfigKeys.newBooleanConfigKey("simulateExternalMonitoring", "", true);
+
+    public static final ConfigKey<Boolean> SKIP_SSH_ON_START = ConfigKeys.newBooleanConfigKey("skipSshOnStart", "", false);
+
+    public static final String WAR_PATH = "classpath://hello-world.war";
+    public static final String DB_TABLE = "visitors";
+    public static final String DB_USERNAME = "brooklyn";
+    public static final String DB_PASSWORD = "br00k11n";
+    public static final boolean USE_HTTPS = false;
+    
+    @Override
+    public void init() {
+        MySqlNode mysql = addChild(
+                EntitySpec.create(MySqlNode.class)
+                .impl(SimulatedMySqlNodeImpl.class));
+
+        ControlledDynamicWebAppCluster web = addChild(
+                EntitySpec.create(ControlledDynamicWebAppCluster.class)
+                .configure(ControlledDynamicWebAppCluster.MEMBER_SPEC, EntitySpec.create(JBoss7Server.class).impl(SimulatedJBoss7ServerImpl.class))
+                .configure(ControlledDynamicWebAppCluster.CONTROLLER_SPEC, EntitySpec.create(NginxController.class).impl(SimulatedNginxControllerImpl.class))
+                        .configure(WebAppService.HTTP_PORT, PortRanges.fromString("8080+"))
+                        .configure(JavaWebAppService.ROOT_WAR, WAR_PATH)
+                        .configure(JavaEntityMethods.javaSysProp("brooklyn.example.db.url"), 
+                                formatString("jdbc:%s%s?user=%s\\&password=%s", 
+                                        attributeWhenReady(mysql, MySqlNode.DATASTORE_URL), DB_TABLE, DB_USERNAME, DB_PASSWORD))
+                        .configure(DynamicCluster.INITIAL_SIZE, 2)
+                        .configure(WebAppService.ENABLED_PROTOCOLS, ImmutableSet.of(USE_HTTPS ? "https" : "http")) );
+
+        web.getCluster().addPolicy(AutoScalerPolicy.builder().
+                metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW_PER_NODE).
+                metricRange(10, 100).
+                sizeRange(2, 5).
+                build());
+
+        addEnricher(Enrichers.builder()
+                .propagating(Attributes.MAIN_URI, WebAppServiceConstants.ROOT_URL,
+                        DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW,
+                        HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW)
+                .from(web)
+                .build());
+        
+        addEnricher(Enrichers.builder()
+                .aggregating(Startable.SERVICE_UP)
+                .publishing(Startable.SERVICE_UP)
+                .fromHardcodedProducers(ImmutableList.of(web, mysql))
+                .computing(new Function<Collection<Boolean>, Boolean>() {
+                    @Override public Boolean apply(Collection<Boolean> input) {
+                        return input != null && input.size() == 2 && MutableSet.copyOf(input).equals(ImmutableSet.of(true));
+                    }})
+                .build());
+    }
+    
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", "localhost");
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                 .application(EntitySpec.create(StartableApplication.class, SimulatedTheeTierApp.class)
+                         .displayName("Brooklyn WebApp Cluster with Database example"))
+                 .webconsolePort(port)
+                 .location(location)
+                 .start();
+             
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/Monitor.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/Monitor.java b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/Monitor.java
new file mode 100644
index 0000000..7a3ea42
--- /dev/null
+++ b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/Monitor.java
@@ -0,0 +1,261 @@
+/*
+ * 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.qa.longevity;
+
+import static org.apache.brooklyn.qa.longevity.StatusRecorder.Factory.chain;
+import static org.apache.brooklyn.qa.longevity.StatusRecorder.Factory.noop;
+import static org.apache.brooklyn.qa.longevity.StatusRecorder.Factory.toFile;
+import static org.apache.brooklyn.qa.longevity.StatusRecorder.Factory.toLog;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.collections.TimeWindowedList;
+import brooklyn.util.collections.TimestampedValue;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Range;
+import com.google.common.io.Files;
+
+public class Monitor {
+
+    private static final Logger LOG = LoggerFactory.getLogger(Monitor.class);
+    
+    private static final int checkPeriodMs = 1000;
+
+    private static final OptionParser parser = new OptionParser() {
+        {
+            acceptsAll(ImmutableList.of("help", "?", "h"), "show help");
+            accepts("webUrl", "Web-app url")
+                    .withRequiredArg().ofType(URL.class);
+            accepts("brooklynPid", "Brooklyn pid")
+                    .withRequiredArg().ofType(Integer.class);
+            accepts("logFile", "Brooklyn log file")
+                    .withRequiredArg().ofType(File.class);
+            accepts("logGrep", "Grep in log file (defaults to 'SEVERE|ERROR|WARN|Exception|Error'")
+                    .withRequiredArg().ofType(String.class);
+            accepts("logGrepExclusionsFile", "File of expressions to be ignored in log file")
+                    .withRequiredArg().ofType(File.class);
+            accepts("webProcesses", "Name (for `ps ax | grep` of web-processes")
+                    .withRequiredArg().ofType(String.class);
+            accepts("numWebProcesses", "Number of web-processes expected (e.g. 1 or 1-3)")
+                    .withRequiredArg().ofType(String.class);
+            accepts("webProcessesCyclingPeriod", "The period (in seconds) for cycling through the range of numWebProcesses")
+                    .withRequiredArg().ofType(Integer.class);
+            accepts("outFile", "File to write monitor status info")
+                    .withRequiredArg().ofType(File.class);
+            accepts("abortOnError", "Exit the JVM on error, with exit code 1")
+                    .withRequiredArg().ofType(Boolean.class);
+        }
+    };
+
+    public static void main(String[] argv) throws InterruptedException, IOException {
+        OptionSet options = parse(argv);
+
+        if (options == null || options.has("help")) {
+            parser.printHelpOn(System.out);
+            System.exit(0);
+        }
+
+        MonitorPrefs prefs = new MonitorPrefs();
+        prefs.webUrl = options.hasArgument("webUrl") ? (URL) options.valueOf("webUrl") : null;
+        prefs.brooklynPid = options.hasArgument("brooklynPid") ? (Integer) options.valueOf("brooklynPid") : -1;
+        prefs.logFile = options.hasArgument("logFile") ? (File) options.valueOf("logFile") : null;
+        prefs.logGrep = options.hasArgument("logGrep") ? (String) options.valueOf("logGrep") : "SEVERE|ERROR|WARN|Exception|Error";
+        prefs.logGrepExclusionsFile = options.hasArgument("logGrepExclusionsFile") ? (File) options.valueOf("logGrepExclusionsFile") : null;
+        prefs.webProcessesRegex = options.hasArgument("webProcesses") ? (String) options.valueOf("webProcesses") : null;
+        prefs.numWebProcesses = options.hasArgument("numWebProcesses") ? parseRange((String) options.valueOf("numWebProcesses")) : null;
+        prefs.webProcessesCyclingPeriod = options.hasArgument("webProcessesCyclingPeriod") ? (Integer) options.valueOf("webProcessesCyclingPeriod") : -1;
+        prefs.outFile = options.hasArgument("outFile") ? (File) options.valueOf("outFile") : null;
+        prefs.abortOnError = options.hasArgument("abortOnError") ? (Boolean) options.valueOf("abortOnError") : false;
+        Monitor main = new Monitor(prefs, MonitorListener.NOOP);
+        main.start();
+    }
+
+    private static Range<Integer> parseRange(String range) {
+        if (range.contains("-")) {
+            String[] parts = range.split("-");
+            return Range.closed(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
+        } else {
+            return Range.singleton(Integer.parseInt(range));
+        }
+    }
+    
+    private static OptionSet parse(String...argv) {
+        try {
+            return parser.parse(argv);
+        } catch (Exception e) {
+            System.out.println("Error in parsing options: " + e.getMessage());
+            return null;
+        }
+    }
+
+    private final MonitorPrefs prefs;
+    private final StatusRecorder recorder;
+    private final MonitorListener listener;
+    
+    public Monitor(MonitorPrefs prefs, MonitorListener listener) {
+        this.prefs = prefs;
+        this.listener = listener;
+        this.recorder = chain(toLog(LOG), (prefs.outFile != null ? toFile(prefs.outFile) : noop()));
+    }
+
+    private void start() throws IOException {
+        LOG.info("Monitoring: "+prefs);
+        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+
+        final AtomicReference<List<String>> previousLogLines = new AtomicReference<List<String>>(Collections.<String>emptyList());
+        final TimeWindowedList<Integer> numWebProcessesHistory = new TimeWindowedList<Integer>(
+                ImmutableMap.of("timePeriod", Duration.seconds(prefs.webProcessesCyclingPeriod), "minExpiredVals", 1));
+        final Set<String> logGrepExclusions = ImmutableSet.copyOf(Files.readLines(prefs.logGrepExclusionsFile, Charsets.UTF_8));
+        
+        executor.scheduleAtFixedRate(new Runnable() {
+                @Override public void run() {
+                    StatusRecorder.Record record = new StatusRecorder.Record();
+                    StringBuilder failureMsg = new StringBuilder();
+                    try {
+                        if (prefs.brooklynPid > 0) {
+                            boolean pidRunning = MonitorUtils.isPidRunning(prefs.brooklynPid, "java");
+                            MonitorUtils.MemoryUsage memoryUsage = MonitorUtils.getMemoryUsage(prefs.brooklynPid, ".*brooklyn.*", 1000);
+                            record.put("pidRunning", pidRunning);
+                            record.put("totalMemoryBytes", memoryUsage.getTotalMemoryBytes());
+                            record.put("totalMemoryInstances", memoryUsage.getTotalInstances());
+                            record.put("instanceCounts", memoryUsage.getInstanceCounts());
+                            
+                            if (!pidRunning) {
+                                failureMsg.append("pid "+prefs.brooklynPid+" is not running"+"\n");
+                            }
+                        }
+                        if (prefs.webUrl != null) {
+                            boolean webUrlUp = MonitorUtils.isUrlUp(prefs.webUrl);
+                            record.put("webUrlUp", webUrlUp);
+                            
+                            if (!webUrlUp) {
+                                failureMsg.append("web URL "+prefs.webUrl+" is not available"+"\n");
+                            }
+                        }
+                        if (prefs.logFile != null) {
+                            List<String> logLines = MonitorUtils.searchLog(prefs.logFile, prefs.logGrep, logGrepExclusions);
+                            List<String> newLogLines = getAdditions(previousLogLines.get(), logLines);
+                            previousLogLines.set(logLines);
+                            record.put("logLines", newLogLines);
+                            
+                            if (newLogLines.size() > 0) {
+                                failureMsg.append("Log contains warnings/errors: "+newLogLines+"\n");
+                            }
+                        }
+                        if (prefs.webProcessesRegex != null) {
+                            List<Integer> pids = MonitorUtils.getRunningPids(prefs.webProcessesRegex, "--webProcesses");
+                            pids.remove((Object)MonitorUtils.findOwnPid());
+                            
+                            record.put("webPids", pids);
+                            record.put("numWebPids", pids.size());
+                            numWebProcessesHistory.add(pids.size());
+                            
+                            if (prefs.numWebProcesses != null) {
+                                boolean numWebPidsInRange = prefs.numWebProcesses.apply(pids.size());
+                                record.put("numWebPidsInRange", numWebPidsInRange);
+                                
+                                if (!numWebPidsInRange) {
+                                    failureMsg.append("num web processes out-of-range: pids="+pids+"; size="+pids.size()+"; expected="+prefs.numWebProcesses);
+                                }
+                            
+                                if (prefs.webProcessesCyclingPeriod > 0) {
+                                    List<TimestampedValue<Integer>> values = numWebProcessesHistory.getValues();
+                                    long valuesTimeRange = (values.get(values.size()-1).getTimestamp() - values.get(0).getTimestamp());
+                                    if (values.size() > 0 && valuesTimeRange > SECONDS.toMillis(prefs.webProcessesCyclingPeriod)) {
+                                        int min = -1;
+                                        int max = -1;
+                                        for (TimestampedValue<Integer> val : values) {
+                                            min = (min < 0) ? val.getValue() : Math.min(val.getValue(), min);
+                                            max = Math.max(val.getValue(), max);
+                                        }
+                                        record.put("minWebSizeInPeriod", min);
+                                        record.put("maxWebSizeInPeriod", max);
+                                        
+                                        if (min > prefs.numWebProcesses.lowerEndpoint() || max < prefs.numWebProcesses.upperEndpoint()) {
+                                            failureMsg.append("num web processes not increasing/decreasing correctly: " +
+                                                    "pids="+pids+"; size="+pids.size()+"; cyclePeriod="+prefs.webProcessesCyclingPeriod+
+                                                    "; expectedRange="+prefs.numWebProcesses+"; min="+min+"; max="+max+"; history="+values);
+                                        }
+                                    } else {
+                                        int numVals = values.size();
+                                        long startTime = (numVals > 0) ? values.get(0).getTimestamp() : 0;
+                                        long endTime = (numVals > 0) ? values.get(values.size()-1).getTimestamp() : 0;
+                                        LOG.info("Insufficient vals in time-window to determine cycling behaviour over period ("+prefs.webProcessesCyclingPeriod+"secs): "+
+                                                "numVals="+numVals+"; startTime="+startTime+"; endTime="+endTime+"; periodCovered="+(endTime-startTime)/1000);
+                                    }
+                                }
+                            }
+                        }
+                        
+                    } catch (Throwable t) {
+                        LOG.error("Error during periodic checks", t);
+                        throw Throwables.propagate(t);
+                    }
+                    
+                    try {
+                        recorder.record(record);
+                        listener.onRecord(record);
+                        
+                        if (failureMsg.length() > 0) {
+                            listener.onFailure(record, failureMsg.toString());
+                            
+                            if (prefs.abortOnError) {
+                                LOG.error("Aborting on error: "+failureMsg);
+                                System.exit(1);
+                            }
+                        }
+                        
+                    } catch (Throwable t) {
+                        LOG.warn("Error recording monitor info ("+record+")", t);
+                        throw Throwables.propagate(t);
+                    }
+                }
+            }, 0, checkPeriodMs, TimeUnit.MILLISECONDS);
+    }
+    
+    // TODO What is the guava equivalent? Don't want Set.difference, because duplicates/ordered.
+    private static List<String> getAdditions(List<String> prev, List<String> next) {
+        List<String> result = Lists.newArrayList(next);
+        result.removeAll(prev);
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorListener.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorListener.java b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorListener.java
new file mode 100644
index 0000000..11fdd3f
--- /dev/null
+++ b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorListener.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.qa.longevity;
+
+import org.apache.brooklyn.qa.longevity.StatusRecorder.Record;
+
+public interface MonitorListener {
+
+    public static final MonitorListener NOOP = new MonitorListener() {
+        @Override public void onRecord(Record record) {
+        }
+        @Override public void onFailure(Record record, String msg) {
+        }
+    };
+    
+    public void onRecord(Record record);
+    
+    public void onFailure(Record record, String msg);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorPrefs.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorPrefs.java b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorPrefs.java
new file mode 100644
index 0000000..4d74045
--- /dev/null
+++ b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorPrefs.java
@@ -0,0 +1,54 @@
+/*
+ * 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.qa.longevity;
+
+import java.io.File;
+import java.net.URL;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Range;
+
+public class MonitorPrefs {
+
+    public URL webUrl;
+    public int brooklynPid;
+    public File logFile;
+    public String logGrep;
+    public File logGrepExclusionsFile;
+    public String webProcessesRegex;
+    public Range<Integer> numWebProcesses;
+    public int webProcessesCyclingPeriod;
+    public File outFile;
+    public boolean abortOnError;
+    
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("webUrl", webUrl)
+                .add("brooklynPid", brooklynPid)
+                .add("logFile", logFile)
+                .add("logGrep", logGrep)
+                .add("logGrepExclusionsFile", logGrepExclusionsFile)
+                .add("outFile", outFile)
+                .add("webProcessesRegex", webProcessesRegex)
+                .add("numWebProcesses", numWebProcesses)
+                .add("webProcessesCyclingPeriod", webProcessesCyclingPeriod)
+                .toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorUtils.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorUtils.java b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorUtils.java
new file mode 100644
index 0000000..54acb3b
--- /dev/null
+++ b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorUtils.java
@@ -0,0 +1,329 @@
+/*
+ * 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.qa.longevity;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.http.HttpTool;
+import brooklyn.util.http.HttpToolResponse;
+import brooklyn.util.stream.StreamGobbler;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.io.ByteStreams;
+
+public class MonitorUtils {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MonitorUtils.class);
+
+    private static volatile int ownPid = -1;
+
+    /**
+     * Confirm can read from URL.
+     *
+     * @param url
+     */
+    public static boolean isUrlUp(URL url) {
+        try {
+            HttpToolResponse result = HttpTool.httpGet(
+                    HttpTool.httpClientBuilder().trustAll().build(), 
+                    URI.create(url.toString()), 
+                    ImmutableMap.<String,String>of());
+            int statuscode = result.getResponseCode();
+
+            if (statuscode != 200) {
+                LOG.info("Error reading URL {}: {}, {}", new Object[]{url, statuscode, result.getReasonPhrase()});
+                return false;
+            } else {
+                return true;
+            }
+        } catch (Exception e) {
+            LOG.info("Error reading URL {}: {}", url, e);
+            return false;
+        }
+    }
+
+    public static boolean isPidRunning(int pid) {
+        return isPidRunning(pid, null);
+    }
+
+    /**
+     * Confirm the given pid is running, and that the the process matches the given regex.
+     *
+     * @param pid
+     * @param regex
+     */
+    public static boolean isPidRunning(int pid, String regex) {
+        Process process = exec("ps -p " + pid);
+        String out = waitFor(process);
+         if (process.exitValue() > 0) {
+            String err = toString(process.getErrorStream());
+            LOG.info(String.format("pid %s not running: %s", pid, err));
+            return false;
+        }
+
+        if (regex != null) {
+            String regex2 = "^\\s*" + pid + ".*" + regex;
+            boolean found = false;
+            for (String line : out.split("\n")) {
+                if (hasAtLeastOneMatch(line, regex2)) {
+                    found = true;
+                    break;
+                }
+            }
+
+            if (!found) {
+                String txt = toString(process.getInputStream());
+                LOG.info("process did not match regular expression: "+txt);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static boolean hasAtLeastOneMatch(String line, String regex) {
+        return Pattern.matches(".*"+regex+".*", line);
+    }
+
+    private static String toString(InputStream in){
+        try {
+            byte[] bytes = ByteStreams.toByteArray(in);
+            return new String(bytes);
+        } catch (IOException e) {
+            throw Throwables.propagate(e);
+        }
+
+    }
+
+    public static List<Integer> getRunningPids(String regex) {
+        return getRunningPids(regex, null);
+    }
+
+    /**
+     * Confirm the given pid is running, and that the the process matches the given regex.
+     *
+     * @param regex
+     * @param excludingRegex
+     */
+    public static List<Integer> getRunningPids(String regex, String excludingRegex) {
+        Process process = exec("ps ax");
+        String out = waitFor(process);
+
+        List<Integer> result = new LinkedList<Integer>();
+        for (String line : out.split("\n")) {
+            if (excludingRegex != null && hasAtLeastOneMatch(line, excludingRegex)) {
+                continue;
+            }
+            if (hasAtLeastOneMatch(line, regex)) {
+                String[] linesplit = line.trim().split("\\s+");
+                result.add(Integer.parseInt(linesplit[0]));
+            }
+        }
+        return result;
+    }
+
+    public static MemoryUsage getMemoryUsage(int pid){
+          return getMemoryUsage(pid, null,0);
+    }
+
+    /**
+     * @param pid
+     */
+    public static MemoryUsage getMemoryUsage(int pid, String clazzRegexOfInterest, int minInstancesOfInterest) {
+        Process process = exec(String.format("jmap -histo %s", pid));
+        String out = waitFor(process);
+
+        Map<String, Integer> instanceCounts = Maps.newLinkedHashMap();
+        long totalInstances=0;
+        long totalMemoryBytes=0;
+
+        for (String line : out.split("\n")) {
+            if (clazzRegexOfInterest!=null && hasAtLeastOneMatch(line, clazzRegexOfInterest)) {
+                // Format is:
+                //   num     #instances         #bytes  class name
+                //   1:           43506        8047096  example.MyClazz
+
+                String[] parts = line.trim().split("\\s+");
+                String clazz = parts[3];
+                int instanceCount = Integer.parseInt(parts[1]);
+                if (instanceCount >= minInstancesOfInterest) {
+                    instanceCounts.put(clazz, instanceCount);
+                }
+            }
+            if (hasAtLeastOneMatch(line, "^Total.*")) {
+                String[] parts = line.split("\\s+");
+                totalInstances = Long.parseLong(parts[1]);
+                totalMemoryBytes = Long.parseLong(parts[2]);
+            }
+        }
+
+        return new MemoryUsage(totalInstances, totalMemoryBytes, instanceCounts);
+    }
+
+    public static class MemoryUsage {
+        final long totalInstances;
+        final long totalMemoryBytes;
+        final Map<String, Integer> instanceCounts;
+
+        MemoryUsage(long totalInstances, long totalMemoryBytes, Map<String, Integer> instanceCounts) {
+            this.totalInstances = totalInstances;
+            this.totalMemoryBytes = totalMemoryBytes;
+            this.instanceCounts = instanceCounts;
+        }
+
+        public String toString() {
+            return Objects.toStringHelper(this)
+                    .add("totalInstances", totalInstances)
+                    .add("totalMemoryBytes", totalMemoryBytes)
+                    .add("instanceCounts", instanceCounts)
+                    .toString();
+        }
+
+        public long getTotalInstances() {
+            return totalInstances;
+        }
+
+        public long getTotalMemoryBytes() {
+            return totalMemoryBytes;
+        }
+
+        public Map<String, Integer> getInstanceCounts() {
+            return instanceCounts;
+        }
+    }
+
+    public static List<String> searchLog(File file, String grepOfInterest) {
+        return searchLog(file, grepOfInterest, new LinkedHashSet<String>());
+    }
+
+    /**
+     * Find lines in the given file that match given given regex.
+     *
+     * @param file
+     * @param grepOfInterest
+     */
+    public static List<String> searchLog(File file, String grepOfInterest, Set<String> grepExclusions) {
+        Process process = exec(String.format("grep -E %s %s", grepOfInterest, file.getAbsoluteFile()));
+        String out = waitFor(process);
+
+        // TODO Annoying that String.split() returns size 1 when empty string; lookup javadoc when back online...
+        if (out.length() == 0) return Collections.<String>emptyList();
+
+        List<String> result = new ArrayList<String>();
+        for (String line : out.trim().split("\n")) {
+            boolean excluded = false;
+            for (String exclusion : grepExclusions) {
+                if (!isNullOrEmpty(exclusion) && hasAtLeastOneMatch(line, exclusion)) {
+                    excluded = true;
+                }
+            }
+            if (!excluded) {
+                result.add(line);
+            }
+        }
+        return result;
+    }
+
+    public static Process exec(String cmd) {
+        LOG.info("executing cmd: " + cmd);
+
+        try {
+            return Runtime.getRuntime().exec(cmd);
+        } catch (IOException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    public static class ProcessHasStderr extends IllegalStateException {
+        private static final long serialVersionUID = -937871002993888405L;
+        
+        byte[] stderrBytes;
+        public ProcessHasStderr(byte[] stderrBytes) {
+            this("Process printed to stderr: " + new String(stderrBytes), stderrBytes);
+        }
+        public ProcessHasStderr(String message, byte[] stderrBytes) {
+            super(message);
+            this.stderrBytes = stderrBytes;
+        }
+    }
+    
+    /**
+     * Waits for the given process to complete, consuming its stdout and returning it as a string.
+     * If there is any output on stderr an exception will be thrown.
+     */
+    public static String waitFor(Process process) {
+        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+        @SuppressWarnings("resource") //Closeable doesn't seem appropriate for StreamGobbler since it isn't expected to be called every time
+        StreamGobbler gobblerOut = new StreamGobbler(process.getInputStream(), bytesOut, null);
+        gobblerOut.start();
+
+        ByteArrayOutputStream bytesErr = new ByteArrayOutputStream();
+        @SuppressWarnings("resource")
+        StreamGobbler gobblerErr = new StreamGobbler(process.getErrorStream(), bytesErr, null);
+        gobblerErr.start();
+
+        try {
+            process.waitFor();
+            gobblerOut.blockUntilFinished();
+            gobblerErr.blockUntilFinished();
+
+            if (bytesErr.size() > 0) {
+                throw new ProcessHasStderr(bytesErr.toByteArray());
+            }
+
+            return new String(bytesOut.toByteArray());
+        } catch (Exception e) {
+            throw Throwables.propagate(e);
+        } finally {
+            if (gobblerOut.isAlive()) gobblerOut.interrupt();
+            if (gobblerErr.isAlive()) gobblerErr.interrupt();
+        }
+    }
+
+    public static int findOwnPid() throws IOException {
+        if (ownPid >= 0) return ownPid;
+
+        String[] cmd = new String[]{"bash", "-c", "echo $PPID"};
+        Process process = Runtime.getRuntime().exec(cmd);
+        String out = MonitorUtils.waitFor(process);
+        ownPid = Integer.parseInt(out.trim());
+        return ownPid;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/StatusRecorder.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/StatusRecorder.java b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/StatusRecorder.java
new file mode 100644
index 0000000..210e0a2
--- /dev/null
+++ b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/StatusRecorder.java
@@ -0,0 +1,130 @@
+/*
+ * 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.qa.longevity;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import org.slf4j.Logger;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.Maps;
+import com.google.common.io.Files;
+
+public interface StatusRecorder {
+
+    public void record(Record record) throws IOException;
+    
+    public static class Factory {
+        public static final StatusRecorder NOOP = new StatusRecorder() {
+            @Override public void record(Record record) {}
+        };
+        
+        public static StatusRecorder noop() {
+            return NOOP;
+        }
+        public static StatusRecorder toFile(File outFile) {
+            return new FileBasedStatusRecorder(outFile);
+        }
+        public static StatusRecorder toSysout() {
+            return new SysoutBasedStatusRecorder();
+        }
+        public static StatusRecorder toLog(Logger log) {
+            return new LogBasedStatusRecorder(log);
+        }
+        public static StatusRecorder chain(StatusRecorder...recorders) {
+            return new ChainingStatusRecorder(recorders);
+        }
+    }
+    
+    public static class Record {
+        private final Map<String,Object> fields = Maps.newLinkedHashMap();
+        
+        public void putAll(Map<String,?> entries) {
+            fields.putAll(entries);
+        }
+        
+        public void putAll(String keyPrefix, Map<String,?> entries) {
+            for (Map.Entry<String,?> entry : entries.entrySet()) {
+                fields.put(keyPrefix+entry.getKey(), entry.getValue());
+            }
+        }
+        
+        public void put(String key, Object val) {
+            fields.put(key, val);
+        }
+        
+        @Override
+        public String toString() {
+            return fields.toString();
+        }
+    }
+    
+    public static class FileBasedStatusRecorder implements StatusRecorder {
+        private final File outFile;
+    
+        public FileBasedStatusRecorder(File outFile) {
+            this.outFile = outFile;
+        }
+        
+        @Override
+        public void record(Record record) throws IOException {
+            Files.append(record.fields.toString()+"\n", outFile, Charsets.UTF_8);
+        }
+    }
+    
+    public static class SysoutBasedStatusRecorder implements StatusRecorder {
+        public SysoutBasedStatusRecorder() {
+        }
+        
+        @Override
+        public void record(Record record) {
+            System.out.println(record.fields);
+        }
+    }
+    
+    public static class LogBasedStatusRecorder implements StatusRecorder {
+        private final Logger log;
+
+        public LogBasedStatusRecorder(Logger log) {
+            this.log = log;
+        }
+        
+        @Override
+        public void record(Record record) {
+            log.info("{}", record.fields);
+        }
+    }
+    
+    public static class ChainingStatusRecorder implements StatusRecorder {
+        private final StatusRecorder[] recorders;
+
+        public ChainingStatusRecorder(StatusRecorder... recorders) {
+            this.recorders = recorders;
+        }
+        
+        @Override
+        public void record(Record record) throws IOException {
+            for (StatusRecorder recorder : recorders) {
+                recorder.record(record);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/test/java/brooklyn/qa/load/LoadTest.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/test/java/brooklyn/qa/load/LoadTest.java b/usage/qa/src/test/java/brooklyn/qa/load/LoadTest.java
deleted file mode 100644
index 16a0195..0000000
--- a/usage/qa/src/test/java/brooklyn/qa/load/LoadTest.java
+++ /dev/null
@@ -1,243 +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.qa.load;
-
-import static org.testng.Assert.assertEquals;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-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.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.rebind.persister.PersistMode;
-import brooklyn.entity.trait.Startable;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.location.Location;
-import brooklyn.management.ManagementContext;
-import brooklyn.management.ha.HighAvailabilityMode;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.test.PerformanceTestUtils;
-import brooklyn.util.os.Os;
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.common.io.Files;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-
-/**
- * Customers ask about the scalability of Brooklyn. These load tests investigate how many 
- * concurrent apps can be deployed and managed by a single Brooklyn management node.
- * 
- * The apps are "simulated" in that they don't create the underlying resources 
- * (we are not checking if the test machine can run 100s of app-servers simultaneously!) 
- * The install/customize/launch will instead execute ssh commands of comparable length,
- * but that just echo rather than execute the actual commands.
- * 
- * "SIMULATE_EXTERNAL_MONITORING" means that we do not poll the entities directly (over ssh, http or 
- * whatever). Instead we simulate the metrics being injected directly to be set on the entity (e.g. 
- * having been collected from a Graphite server).
- * 
- * "SKIP_SSH_ON_START" means don't do the normal install+customize+launch ssh commands. Instead, just
- * startup the entities so we can monitor their resource usage.
- */
-public class LoadTest {
-
-    // TODO Could/should issue provisioning request through REST api, rather than programmatically; 
-    // and poll to detect completion.
-    
-    /*
-     * Useful commands when investigating:
-     *     LOG_FILE=usage/qa/brooklyn-camp-tests.log
-     *     grep -E "OutOfMemoryError|[P|p]rovisioning time|sleeping before|CPU fraction|LoadTest using" $LOG_FILE | less
-     *     grep -E "OutOfMemoryError|[P|p]rovisioning time" $LOG_FILE; grep "CPU fraction" $LOG_FILE | tail -1; grep "LoadTest using" $LOG_FILE | tail -1
-     *     grep -E "OutOfMemoryError|LoadTest using" $LOG_FILE
-     */
-    private static final Logger LOG = LoggerFactory.getLogger(LoadTest.class);
-
-    private File persistenceDir;
-    private BrooklynLauncher launcher;
-    private String webServerUrl;
-    private ManagementContext managementContext;
-    private ListeningExecutorService executor;
-    private Future<?> cpuFuture;
-    
-    private Location localhost;
-    
-    List<Duration> provisioningTimes;
-
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        // Create management node
-        persistenceDir = Files.createTempDir();
-        launcher = BrooklynLauncher.newInstance()
-                .persistMode(PersistMode.CLEAN)
-                .highAvailabilityMode(HighAvailabilityMode.MASTER)
-                .persistenceDir(persistenceDir)
-                .start();
-        webServerUrl = launcher.getServerDetails().getWebServerUrl();
-        managementContext = launcher.getServerDetails().getManagementContext();
-
-        localhost = managementContext.getLocationRegistry().resolve("localhost");
-        
-        provisioningTimes = Collections.synchronizedList(Lists.<Duration>newArrayList());
-
-        // Create executors
-        executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
-
-        // Monitor utilisation (memory/CPU) while tests run
-        executor.submit(new Callable<Void>() {
-            public Void call() {
-                try {
-                    while (true) {
-                        managementContext.getExecutionManager(); // force GC to be instantiated
-                        String usage = ((LocalManagementContext)managementContext).getGarbageCollector().getUsageString();
-                        LOG.info("LoadTest using "+usage);
-                        Thread.sleep(1000);
-                    }
-                } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt(); // exit gracefully
-                } catch (Exception e) {
-                    LOG.error("Error getting usage info", e);
-                }
-                return null;
-            }});
-        
-        cpuFuture = PerformanceTestUtils.sampleProcessCpuTime(Duration.ONE_SECOND, "during testProvisionAppsConcurrently");
-
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (cpuFuture != null) cpuFuture.cancel(true);
-        if (executor != null) executor.shutdownNow();
-        if (launcher != null) launcher.terminate();
-        if (persistenceDir != null) Os.deleteRecursively(persistenceDir);
-    }
-    
-    /**
-     * Creates multiple apps simultaneously. 
-     * 
-     * Long-term target is 50 concurrent provisioning requests (which may be issued while there are
-     * many existing applications under management). Until we reach that point, we can partition the
-     * load across multiple (separate) brooklyn management nodes.
-     *   TODO TBD: is that 50 VMs worth, or 50 apps with 4 VMs in each? 
-     * 
-     * TODO Does not measure the cost of jclouds for creating all the VMs/containers.
-     */
-    @Test(groups="Acceptance")
-    public void testLocalhostProvisioningAppsConcurrently() throws Exception {
-        final int NUM_CONCURRENT_APPS_PROVISIONING = 20; 
-        
-        List<ListenableFuture<StartableApplication>> futures = Lists.newArrayList();
-        for (int i = 0; i < NUM_CONCURRENT_APPS_PROVISIONING; i++) {
-            ListenableFuture<StartableApplication> future = executor.submit(newProvisionAppTask(managementContext, 
-                    EntitySpec.create(StartableApplication.class, SimulatedTheeTierApp.class)
-                            .configure(SimulatedTheeTierApp.SIMULATE_EXTERNAL_MONITORING, true)
-                            .displayName("Simulated app "+i)));
-            futures.add(future);
-        }
-        
-        List<StartableApplication> apps = Futures.allAsList(futures).get();
-        
-        for (StartableApplication app : apps) {
-            assertEquals(app.getAttribute(Startable.SERVICE_UP), (Boolean)true);
-        }
-    }
-    
-    /**
-     * Creates many apps, to monitor resource usage etc.
-     * 
-     * "SIMULATE_EXTERNAL_MONITORING" means that we do not poll the entities directly (over ssh, http or 
-     * whatever). Instead we simulate the metrics being injected directly to be set on the entity (e.g. 
-     * having been collected from a Graphite server).
-     * 
-     * Long-term target is 2500 VMs under management.
-     * Until we reach that point, we can partition the load across multiple (separate) brooklyn management nodes.
-     */
-    @Test(groups="Acceptance")
-    public void testLocalhostManyApps() throws Exception {
-        final int NUM_APPS = 630; // target is 2500 VMs; each blueprint has 4 (rounding up)
-        final int NUM_APPS_PER_BATCH = 10;
-        final int SLEEP_BETWEEN_BATCHES = 10*1000;
-        final boolean SKIP_SSH_ON_START = true; // getting ssh errors otherwise!
-        
-        int counter = 0;
-        
-        for (int i = 0; i < NUM_APPS / NUM_APPS_PER_BATCH; i++) {
-            List<ListenableFuture<StartableApplication>> futures = Lists.newArrayList();
-            for (int j = 0; j < NUM_APPS_PER_BATCH; j++) {
-                ListenableFuture<StartableApplication> future = executor.submit(newProvisionAppTask(
-                        managementContext, 
-                        EntitySpec.create(StartableApplication.class, SimulatedTheeTierApp.class)
-                                .configure(SimulatedTheeTierApp.SIMULATE_EXTERNAL_MONITORING, true)
-                                .configure(SimulatedTheeTierApp.SKIP_SSH_ON_START, SKIP_SSH_ON_START)
-                                .displayName("Simulated app "+(++counter))));
-                futures.add(future);
-            }
-            
-            List<StartableApplication> apps = Futures.allAsList(futures).get();
-            
-            for (StartableApplication app : apps) {
-                assertEquals(app.getAttribute(Startable.SERVICE_UP), (Boolean)true);
-            }
-
-            synchronized (provisioningTimes) {
-                LOG.info("cycle="+i+"; numApps="+counter+": provisioning times: "+provisioningTimes);
-                provisioningTimes.clear();
-            }
-
-            LOG.info("cycle="+i+"; numApps="+counter+": sleeping before next batch of apps");
-            Thread.sleep(SLEEP_BETWEEN_BATCHES);
-        }
-    }
-    
-    protected <T extends StartableApplication> Callable<T> newProvisionAppTask(final ManagementContext managementContext, final EntitySpec<T> entitySpec) {
-        return new Callable<T>() {
-            public T call() {
-                Stopwatch stopwatch = Stopwatch.createStarted();
-                T app = managementContext.getEntityManager().createEntity(entitySpec);
-                Entities.startManagement(app, managementContext);
-                app.start(ImmutableList.of(localhost));
-                Duration duration = Duration.of(stopwatch.elapsed(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
-                LOG.info("Provisioning time: "+duration);
-                provisioningTimes.add(duration);
-
-                return app;
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/test/java/brooklyn/qa/longevity/MonitorUtilsTest.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/test/java/brooklyn/qa/longevity/MonitorUtilsTest.java b/usage/qa/src/test/java/brooklyn/qa/longevity/MonitorUtilsTest.java
deleted file mode 100644
index 2f1e854..0000000
--- a/usage/qa/src/test/java/brooklyn/qa/longevity/MonitorUtilsTest.java
+++ /dev/null
@@ -1,165 +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.qa.longevity;
-
-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 java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.testng.annotations.Test;
-
-import brooklyn.qa.longevity.MonitorUtils.ProcessHasStderr;
-import brooklyn.util.os.Os;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.Files;
-
-public class MonitorUtilsTest {
-
-    @Test(enabled=false, timeOut=1000) // Demonstrates that process.waitFor() hangs for big output streams
-    public void testExecuteAndWaitFor() throws Exception {
-        Process process = createDumpingProcess(false);
-        process.waitFor();
-        fail("Should block while waiting to consume process output");
-    }
-
-    @Test(enabled=false, timeOut=1000) // Demonstrates that process.waitFor() hangs for big err streams
-    public void testExecuteAndWaitForErr() throws Exception {
-        Process process = createDumpingProcess(true);
-        process.waitFor();
-        fail("Should block while waiting to consume process output");
-    }
-
-    @Test(timeOut=1000)
-    public void testExecuteAndWaitForConsumingOutputStream() throws Exception {
-        Process process = createDumpingProcess(false);
-        String out = MonitorUtils.waitFor(process);
-        assertTrue(out.length() > 100000, "out.size="+out.length());
-    }
-
-    @Test(timeOut=1000, expectedExceptions=IllegalStateException.class)
-    public void testExecuteAndWaitForConsumingErrorStream() throws Exception {
-        Process process = createDumpingProcess(true);
-        MonitorUtils.waitFor(process);
-    }
-
-    private Process createDumpingProcess(boolean writeToErr) throws IOException {
-        String errSuffix = writeToErr ? " >&2" : "";
-        //Windows limits the length of the arguments so echo multiple times instead
-        String bigstr = Strings.repeat("a", 8000);
-        String bigcmd = Strings.repeat(getSilentPrefix() + "echo " + bigstr + errSuffix + Os.LINE_SEPARATOR, 15);
-        File file = Os.newTempFile("test-consume", ".bat");
-        file.setExecutable(true);
-        Files.write(bigcmd, file, Charsets.UTF_8);
-        Process process = MonitorUtils.exec(file.getAbsolutePath());
-        return process;
-    }
-
-    @Test(groups="UNIX")
-    public void testFindOwnPid() throws Exception {
-        int ownpid = MonitorUtils.findOwnPid();
-        assertTrue(ownpid > 0, "ownpid=$ownpid");
-        assertTrue(MonitorUtils.isPidRunning(ownpid, "java"),"java is not running");
-    }
-
-    @Test(groups="UNIX")
-    public void testIsPidRunning() throws Exception {
-        int usedPid = MonitorUtils.findOwnPid();
-
-        //the child process will terminate freeing it PID
-        String[] cmd = new String[]{"bash", "-c", "echo $$"};
-        Process process = Runtime.getRuntime().exec(cmd);
-        String out = MonitorUtils.waitFor(process);
-        int unusedPid = Integer.parseInt(out.trim());
-
-        assertTrue(MonitorUtils.isPidRunning(usedPid));
-        assertFalse(MonitorUtils.isPidRunning(unusedPid));
-        
-        try {
-            assertFalse(MonitorUtils.isPidRunning(1234567)); // too large
-        } catch (ProcessHasStderr e) {
-            // expected on osx
-        }
-    }
-
-    @Test(groups="UNIX")
-    public void testGetRunningPids() throws Exception {
-        int ownpid = MonitorUtils.findOwnPid();
-
-        List<Integer> javapids = MonitorUtils.getRunningPids("java");
-
-        assertTrue(javapids.contains(ownpid), "javapids="+javapids+"; ownpid="+ownpid);
-    }
-
-    @Test
-    public void testIsUrlUp() throws Exception {
-        assertFalse(MonitorUtils.isUrlUp(new URL("http://localhost/thispathdoesnotexist")));
-    }
-
-    @Test(groups="UNIX")
-    public void testSearchLog() throws Exception {
-        String fileContents = "line1\nline2\nline3\n";
-        File file = File.createTempFile("monitorUtilsTest.testSearchLog", ".txt");
-        Files.write(fileContents, file, Charsets.UTF_8);
-
-        try {
-            assertEquals(MonitorUtils.searchLog(file, "line1"), Arrays.asList("line1"));
-            assertEquals(MonitorUtils.searchLog(file, "line1|line2"), Arrays.asList("line1", "line2"));
-            assertEquals(MonitorUtils.searchLog(file, "textnotthere"), Collections.emptyList());
-            assertEquals(MonitorUtils.searchLog(file, "line"), Arrays.asList("line1", "line2", "line3"));
-        } finally {
-            file.delete();
-        }
-    }
-
-    @Test(groups="Integration")
-    public void testMemoryUsage() throws Exception {
-        int ownpid = MonitorUtils.findOwnPid();
-
-        MonitorUtils.MemoryUsage memUsage = MonitorUtils.getMemoryUsage(ownpid);
-        assertTrue(memUsage.totalInstances > 0, memUsage.toString());
-        assertTrue(memUsage.totalMemoryBytes > 0, memUsage.toString());
-        assertEquals(memUsage.getInstanceCounts(), Collections.emptyMap());
-
-        MonitorUtils.MemoryUsage memUsage2 = MonitorUtils.getMemoryUsage(ownpid, MonitorUtilsTest.class.getCanonicalName(),0);
-        assertEquals(memUsage2.getInstanceCounts(), ImmutableMap.of(MonitorUtilsTest.class.getCanonicalName(), 1));
-
-        MonitorUtils.MemoryUsage memUsage3 = MonitorUtils.getMemoryUsage(ownpid, MonitorUtilsTest.class.getCanonicalName(), 2);
-        assertEquals(memUsage3.getInstanceCounts(), Collections.emptyMap());
-    }
-
-    private String getSilentPrefix() {
-        if (Os.isMicrosoftWindows()) {
-            return "@";
-        } else {
-            return "";
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/test/java/brooklyn/qa/longevity/webcluster/SinusoidalLoadGenerator.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/test/java/brooklyn/qa/longevity/webcluster/SinusoidalLoadGenerator.java b/usage/qa/src/test/java/brooklyn/qa/longevity/webcluster/SinusoidalLoadGenerator.java
deleted file mode 100644
index 392b976..0000000
--- a/usage/qa/src/test/java/brooklyn/qa/longevity/webcluster/SinusoidalLoadGenerator.java
+++ /dev/null
@@ -1,90 +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.qa.longevity.webcluster;
-
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.enricher.basic.AbstractEnricher;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.EntityLocal;
-import brooklyn.event.AttributeSensor;
-
-import com.google.common.base.Throwables;
-import com.google.common.reflect.TypeToken;
-
-/**
- * Periodically publishes values in the range of 0 to #amplitude. 
- * The value varies sinusoidally over time.
- */
-public class SinusoidalLoadGenerator extends AbstractEnricher {
-
-    private static final Logger LOG = LoggerFactory.getLogger(SinusoidalLoadGenerator.class);
-
-    public static final ConfigKey<AttributeSensor<Double>> TARGET = ConfigKeys.newConfigKey(new TypeToken<AttributeSensor<Double>>() {}, "target");
-    
-    public static final ConfigKey<Long> PUBLISH_PERIOD_MS = ConfigKeys.newLongConfigKey("publishPeriodMs");
-
-    public static final ConfigKey<Long> SIN_PERIOD_MS = ConfigKeys.newLongConfigKey("sinPeriodMs");
-
-    public static final ConfigKey<Double> SIN_AMPLITUDE = ConfigKeys.newDoubleConfigKey("sinAmplitude");
-
-    private final ScheduledExecutorService executor;
-    
-    public SinusoidalLoadGenerator() {
-        this.executor = Executors.newSingleThreadScheduledExecutor();
-    }
-    
-    public SinusoidalLoadGenerator(AttributeSensor<Double> target, long publishPeriodMs, long sinPeriodMs, double sinAmplitude) {
-        config().set(TARGET, target);
-        config().set(PUBLISH_PERIOD_MS, publishPeriodMs);
-        config().set(SIN_PERIOD_MS, sinPeriodMs);
-        config().set(SIN_AMPLITUDE, sinAmplitude);
-        this.executor = Executors.newSingleThreadScheduledExecutor();
-    }
-    
-    @Override
-    public void setEntity(final EntityLocal entity) {
-        super.setEntity(entity);
-        
-        executor.scheduleAtFixedRate(new Runnable() {
-            @Override public void run() {
-                try {
-                    long time = System.currentTimeMillis();
-                    double val = getRequiredConfig(SIN_AMPLITUDE) * (1 + Math.sin( (1.0*time) / getRequiredConfig(SIN_PERIOD_MS) * Math.PI * 2  - Math.PI/2 )) / 2;
-                    entity.setAttribute(getRequiredConfig(TARGET), val);
-                } catch (Throwable t) {
-                    LOG.warn("Error generating sinusoidal-load metric", t);
-                    throw Throwables.propagate(t);
-                }
-            }
-
-        }, 0, getRequiredConfig(PUBLISH_PERIOD_MS), TimeUnit.MILLISECONDS);
-    }
-    
-    @Override
-    public void destroy() {
-        executor.shutdownNow();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/test/java/brooklyn/qa/longevity/webcluster/WebClusterApp.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/test/java/brooklyn/qa/longevity/webcluster/WebClusterApp.java b/usage/qa/src/test/java/brooklyn/qa/longevity/webcluster/WebClusterApp.java
deleted file mode 100644
index a223861..0000000
--- a/usage/qa/src/test/java/brooklyn/qa/longevity/webcluster/WebClusterApp.java
+++ /dev/null
@@ -1,101 +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.qa.longevity.webcluster;
-
-import java.util.List;
-
-import brooklyn.config.BrooklynProperties;
-import brooklyn.enricher.Enrichers;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.proxy.nginx.NginxController;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
-import brooklyn.entity.webapp.jboss.JBoss7Server;
-import brooklyn.event.AttributeSensor;
-import brooklyn.event.basic.Sensors;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.policy.EnricherSpec;
-import brooklyn.policy.autoscaling.AutoScalerPolicy;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.collect.Lists;
-
-public class WebClusterApp extends AbstractApplication {
-
-    static BrooklynProperties config = BrooklynProperties.Factory.newDefault();
-
-    public static final String WAR_PATH = "classpath://hello-world.war";
-
-    private static final long loadCyclePeriodMs = 2 * 60 * 1000L;
-
-    @Override
-    public void initApp() {
-        final AttributeSensor<Double> sinusoidalLoad =
-                Sensors.newDoubleSensor("brooklyn.qa.sinusoidalLoad", "Sinusoidal server load");
-        AttributeSensor<Double> averageLoad =
-                Sensors.newDoubleSensor("brooklyn.qa.averageLoad", "Average load in cluster");
-
-        NginxController nginxController = addChild(EntitySpec.create(NginxController.class)
-                // .configure("domain", "webclusterexample.brooklyn.local")
-                .configure("port", "8000+"));
-
-        EntitySpec<JBoss7Server> jbossSpec = EntitySpec.create(JBoss7Server.class)
-                .configure("httpPort", "8080+")
-                .configure("war", WAR_PATH)
-                .enricher(EnricherSpec.create(SinusoidalLoadGenerator.class)
-                        .configure(SinusoidalLoadGenerator.TARGET, sinusoidalLoad)
-                        .configure(SinusoidalLoadGenerator.PUBLISH_PERIOD_MS, 500L)
-                        .configure(SinusoidalLoadGenerator.SIN_PERIOD_MS, loadCyclePeriodMs)
-                        .configure(SinusoidalLoadGenerator.SIN_AMPLITUDE, 1d));
-
-        ControlledDynamicWebAppCluster web = addChild(EntitySpec.create(ControlledDynamicWebAppCluster.class)
-                .displayName("WebApp cluster")
-                .configure("controller", nginxController)
-                .configure("initialSize", 1)
-                .configure("memberSpec", jbossSpec));
-
-        web.getCluster().addEnricher(Enrichers.builder()
-                .aggregating(sinusoidalLoad)
-                .publishing(averageLoad)
-                .fromMembers()
-                .computingAverage()
-                .build());
-        web.getCluster().addPolicy(AutoScalerPolicy.builder()
-                .metric(averageLoad)
-                .sizeRange(1, 3)
-                .metricRange(0.3, 0.7)
-                .build());
-    }
-    
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", "localhost");
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                .application(EntitySpec.create(StartableApplication.class, WebClusterApp.class).displayName("Brooklyn WebApp Cluster example"))
-                .webconsolePort(port)
-                .location(location)
-                .start();
-         
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}



[10/12] incubator-brooklyn git commit: brooklyn-example-simple-nosql-cluster: add org.apache package prefix

Posted by al...@apache.org.
brooklyn-example-simple-nosql-cluster: add org.apache package prefix


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/44d2dc11
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/44d2dc11
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/44d2dc11

Branch: refs/heads/master
Commit: 44d2dc1163db52a35e1125654f1137f106c196dc
Parents: 0cdb2ed
Author: Ciprian Ciubotariu <ch...@gmx.net>
Authored: Wed Jul 15 11:06:56 2015 +0300
Committer: Ciprian Ciubotariu <ch...@gmx.net>
Committed: Wed Jul 15 18:47:01 2015 +0300

----------------------------------------------------------------------
 .../brooklyn/demo/CumulusRDFApplication.java    | 240 -------------------
 .../demo/HighAvailabilityCassandraCluster.java  |  89 -------
 .../java/brooklyn/demo/ResilientMongoDbApp.java | 106 --------
 .../java/brooklyn/demo/RiakClusterExample.java  |  76 ------
 .../brooklyn/demo/SimpleCassandraCluster.java   |  58 -----
 .../brooklyn/demo/SimpleCouchDBCluster.java     |  36 ---
 .../brooklyn/demo/SimpleMongoDBReplicaSet.java  |  39 ---
 .../java/brooklyn/demo/SimpleRedisCluster.java  |  35 ---
 .../main/java/brooklyn/demo/StormSampleApp.java |  70 ------
 .../brooklyn/demo/WideAreaCassandraCluster.java |  86 -------
 .../brooklyn/demo/CumulusRDFApplication.java    | 240 +++++++++++++++++++
 .../demo/HighAvailabilityCassandraCluster.java  |  89 +++++++
 .../brooklyn/demo/ResilientMongoDbApp.java      | 106 ++++++++
 .../brooklyn/demo/RiakClusterExample.java       |  76 ++++++
 .../brooklyn/demo/SimpleCassandraCluster.java   |  58 +++++
 .../brooklyn/demo/SimpleCouchDBCluster.java     |  36 +++
 .../brooklyn/demo/SimpleMongoDBReplicaSet.java  |  39 +++
 .../brooklyn/demo/SimpleRedisCluster.java       |  35 +++
 .../apache/brooklyn/demo/StormSampleApp.java    |  70 ++++++
 .../brooklyn/demo/WideAreaCassandraCluster.java |  86 +++++++
 .../brooklyn/demo/ha-cassandra-cluster.yaml     |  45 ----
 .../brooklyn/demo/simple-cassandra-cluster.yaml |  28 ---
 .../demo/wide-area-cassandra-cluster.yaml       |  41 ----
 .../brooklyn/demo/ha-cassandra-cluster.yaml     |  45 ++++
 .../brooklyn/demo/simple-cassandra-cluster.yaml |  28 +++
 .../demo/wide-area-cassandra-cluster.yaml       |  41 ++++
 26 files changed, 949 insertions(+), 949 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/CumulusRDFApplication.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/CumulusRDFApplication.java b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/CumulusRDFApplication.java
deleted file mode 100644
index 3379bd4..0000000
--- a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/CumulusRDFApplication.java
+++ /dev/null
@@ -1,240 +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.demo;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.catalog.Catalog;
-import brooklyn.catalog.CatalogConfig;
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.Effector;
-import brooklyn.entity.Entity;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.entity.basic.ServiceStateLogic;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.effector.EffectorBody;
-import brooklyn.entity.effector.Effectors;
-import brooklyn.entity.java.UsesJava;
-import brooklyn.entity.java.UsesJmx;
-import brooklyn.entity.nosql.cassandra.CassandraDatacenter;
-import brooklyn.entity.nosql.cassandra.CassandraFabric;
-import brooklyn.entity.nosql.cassandra.CassandraNode;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.software.SshEffectorTasks;
-import brooklyn.entity.trait.Startable;
-import brooklyn.entity.webapp.JavaWebAppService;
-import brooklyn.entity.webapp.tomcat.TomcatServer;
-import brooklyn.event.SensorEvent;
-import brooklyn.event.SensorEventListener;
-import brooklyn.event.basic.DependentConfiguration;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.location.Location;
-import brooklyn.location.basic.PortRanges;
-import brooklyn.policy.EnricherSpec;
-import brooklyn.policy.PolicySpec;
-import brooklyn.policy.ha.ServiceFailureDetector;
-import brooklyn.policy.ha.ServiceReplacer;
-import brooklyn.policy.ha.ServiceRestarter;
-import brooklyn.util.CommandLineUtil;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.text.StringEscapes.JavaStringEscapes;
-import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.net.HostAndPort;
-
-/** CumulusRDF application with Cassandra cluster. */
-@Catalog(name="Cumulus RDF Application", description="CumulusRDF Application on a Tomcat server using a multi-region Cassandra fabric")
-public class CumulusRDFApplication extends AbstractApplication {
-
-    private static final Logger log = LoggerFactory.getLogger(CumulusRDFApplication.class);
-
-    @CatalogConfig(label="Cumulus Configuration File (URL)", priority=1)
-    public static final ConfigKey<String> CUMULUS_RDF_CONFIG_URL = ConfigKeys.newConfigKey(
-        "cumulus.config.url", "URL for the YAML configuration file for CumulusRDF", "classpath://cumulus.yaml");
-
-    @CatalogConfig(label="Cassandra Thrift Port", priority=1)
-    public static final ConfigKey<Integer> CASSANDRA_THRIFT_PORT = ConfigKeys.newConfigKey(
-        "cumulus.cassandra.port", "Port to contact the Cassandra cluster on", 9160);
-
-    @CatalogConfig(label="Cassandra Cluster Size", priority=1)
-    public static final ConfigKey<Integer> CASSANDRA_CLUSTER_SIZE = ConfigKeys.newConfigKey(
-        "cumulus.cassandra.cluster.size", "Initial size of the Cassandra cluster", 2);
-
-    @CatalogConfig(label="Multi-region Fabric", priority=1)
-    public static final ConfigKey<Boolean> MULTI_REGION_FABRIC = ConfigKeys.newConfigKey(
-        "cumulus.cassandra.fabric", "Deploy a multi-region Cassandra fabric", false);
-
-    // TODO Fails when given two locations
-    // public static final String DEFAULT_LOCATIONS = "[ jclouds:aws-ec2:us-east-1,jclouds:rackspace-cloudservers-uk ]";
-    public static final String DEFAULT_LOCATIONS = "jclouds:aws-ec2:us-east-1";
-
-    private Effector<Void> cumulusConfig = Effectors.effector(Void.class, "cumulusConfig")
-            .description("Configure the CumulusRDF web application")
-            .buildAbstract();
-
-    private Entity cassandra;
-    private TomcatServer webapp;
-    private HostAndPort endpoint;
-    private final Object endpointMutex = new Object();
-
-    /**
-     * Create the application entities:
-     * <ul>
-     * <li>A {@link CassandraFabric} of {@link CassandraDatacenter}s containing {@link CassandraNode}s
-     * <li>A {@link TomcatServer}
-     * </ul>
-     */
-    @Override
-    public void initApp() {
-        // Cassandra cluster
-        EntitySpec<CassandraDatacenter> clusterSpec = EntitySpec.create(CassandraDatacenter.class)
-                .configure(CassandraDatacenter.MEMBER_SPEC, EntitySpec.create(CassandraNode.class)
-                        //FIXME can probably use JMXMP_AND_RMI now, to deploy to GCE and elsewhere
-                        .configure(UsesJmx.JMX_AGENT_MODE, UsesJmx.JmxAgentModes.JMX_RMI_CUSTOM_AGENT)
-                        .configure(UsesJmx.JMX_PORT, PortRanges.fromString("11099+"))
-                        .configure(UsesJmx.RMI_REGISTRY_PORT, PortRanges.fromString("9001+"))
-                        .configure(CassandraNode.THRIFT_PORT, PortRanges.fromInteger(getConfig(CASSANDRA_THRIFT_PORT)))
-                        .enricher(EnricherSpec.create(ServiceFailureDetector.class))
-                        .policy(PolicySpec.create(ServiceRestarter.class)
-                                .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, ServiceFailureDetector.ENTITY_FAILED)))
-                .policy(PolicySpec.create(ServiceReplacer.class)
-                        .configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, ServiceRestarter.ENTITY_RESTART_FAILED));
-
-        if (getConfig(MULTI_REGION_FABRIC)) {
-            cassandra = addChild(EntitySpec.create(CassandraFabric.class)
-                    .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn")
-                    .configure(CassandraDatacenter.INITIAL_SIZE, getConfig(CASSANDRA_CLUSTER_SIZE)) // per location
-                    .configure(CassandraDatacenter.ENDPOINT_SNITCH_NAME, "brooklyn.entity.nosql.cassandra.customsnitch.MultiCloudSnitch")
-                    .configure(CassandraNode.CUSTOM_SNITCH_JAR_URL, "classpath://brooklyn/entity/nosql/cassandra/cassandra-multicloud-snitch.jar")
-                    .configure(CassandraFabric.MEMBER_SPEC, clusterSpec));
-        } else {
-            cassandra = addChild(EntitySpec.create(clusterSpec)
-                    .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn")
-                    .configure(CassandraDatacenter.INITIAL_SIZE, getConfig(CASSANDRA_CLUSTER_SIZE)));
-        }
-
-        // Tomcat web-app server
-        webapp = addChild(EntitySpec.create(TomcatServer.class)
-                .configure(UsesJmx.JMX_AGENT_MODE, UsesJmx.JmxAgentModes.JMX_RMI_CUSTOM_AGENT)
-                .configure(UsesJmx.JMX_PORT, PortRanges.fromString("11099+"))
-                .configure(UsesJmx.RMI_REGISTRY_PORT, PortRanges.fromString("9001+"))
-                .configure(JavaWebAppService.ROOT_WAR, "https://cumulusrdf.googlecode.com/svn/wiki/downloads/cumulusrdf-1.0.1.war")
-                .configure(UsesJava.JAVA_SYSPROPS, MutableMap.of("cumulusrdf.config-file", "/tmp/cumulus.yaml")));
-
-        // Add an effector to tomcat to reconfigure with a new YAML config file
-        ((EntityInternal) webapp).getMutableEntityType().addEffector(cumulusConfig, new EffectorBody<Void>() {
-            @Override
-            public Void call(ConfigBag parameters) {
-                // Process the YAML template given in the application config
-                String url = Entities.getRequiredUrlConfig(CumulusRDFApplication.this, CUMULUS_RDF_CONFIG_URL);
-                Map<String, Object> config;
-                synchronized (endpointMutex) {
-                    config = MutableMap.<String, Object>of("cassandraHostname", endpoint.getHostText(), "cassandraThriftPort", endpoint.getPort());
-                }
-                String contents = TemplateProcessor.processTemplateContents(ResourceUtils.create(CumulusRDFApplication.this).getResourceAsString(url), config);
-                // Copy the file contents to the remote machine
-                return DynamicTasks.queue(SshEffectorTasks.put("/tmp/cumulus.yaml").contents(contents)).get();
-            }
-        });
-
-        // Listen for HOSTNAME changes from the Cassandra fabric to show at least one node is available
-        subscribe(cassandra, CassandraDatacenter.HOSTNAME, new SensorEventListener<String>() {
-            @Override
-            public void onEvent(SensorEvent<String> event) {
-                if (Strings.isNonBlank(event.getValue())) {
-                    synchronized (endpointMutex) {
-                        String hostname = Entities.submit(CumulusRDFApplication.this, DependentConfiguration.attributeWhenReady(cassandra, CassandraDatacenter.HOSTNAME)).getUnchecked();
-                        Integer thriftPort = Entities.submit(CumulusRDFApplication.this, DependentConfiguration.attributeWhenReady(cassandra, CassandraDatacenter.THRIFT_PORT)).getUnchecked();
-                        HostAndPort current = HostAndPort.fromParts(hostname, thriftPort);
-
-                        // Check if the cluster access point has changed
-                        if (!current.equals(endpoint)) {
-                            log.info("Setting cluster endpoint to {}", current.toString());
-                            endpoint = current;
-
-                            // Reconfigure the CumulusRDF application and restart tomcat if necessary
-                            webapp.invoke(cumulusConfig, MutableMap.<String, Object>of());
-                            if (webapp.getAttribute(Startable.SERVICE_UP)) {
-                                webapp.restart();
-                            }
-                        }
-                    }
-                }
-            }
-        });
-    }
-
-    /**
-     * Controls the startup locations for the webapp and the cassandra fabric.
-     *
-     * @see AbstractApplication#start(Collection)
-     */
-    @Override
-    public void start(Collection<? extends Location> locations) {
-        addLocations(locations);
-
-        // The web application only needs to run in one location, use the first
-        // TODO use a multi-region web cluster
-        Collection<? extends Location> first = MutableList.copyOf(Iterables.limit(locations, 1));
-
-        ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
-        try {
-            Entities.invokeEffector(this, cassandra, Startable.START, MutableMap.of("locations", locations)).getUnchecked();
-            Entities.invokeEffector(this, webapp, Startable.START, MutableMap.of("locations", first)).getUnchecked();
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        } finally {
-            ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING);
-        }
-        log.info("Started CumulusRDF in " + locations);
-    }
-
-
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String locations = CommandLineUtil.getCommandLineOption(args, "--locations", DEFAULT_LOCATIONS);
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                .application(EntitySpec.create(StartableApplication.class, CumulusRDFApplication.class).displayName("CumulusRDF application using Cassandra"))
-                .webconsolePort(port)
-                .locations(Strings.isBlank(locations) ? ImmutableList.<String>of() : JavaStringEscapes.unwrapJsonishListIfPossible(locations))
-                .start();
-
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/HighAvailabilityCassandraCluster.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/HighAvailabilityCassandraCluster.java b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/HighAvailabilityCassandraCluster.java
deleted file mode 100644
index a105783..0000000
--- a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/HighAvailabilityCassandraCluster.java
+++ /dev/null
@@ -1,89 +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.demo;
-
-import java.util.List;
-
-import brooklyn.catalog.Catalog;
-import brooklyn.catalog.CatalogConfig;
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.nosql.cassandra.CassandraDatacenter;
-import brooklyn.entity.nosql.cassandra.CassandraNode;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.policy.EnricherSpec;
-import brooklyn.policy.PolicySpec;
-import brooklyn.policy.ha.ServiceFailureDetector;
-import brooklyn.policy.ha.ServiceReplacer;
-import brooklyn.policy.ha.ServiceRestarter;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.collect.Lists;
-
-@Catalog(name="HA Cassandra Cluster Application", description="Deploy a Cassandra cluster with a High Availability architecture.")
-public class HighAvailabilityCassandraCluster extends AbstractApplication {
-
-    public static final String DEFAULT_LOCATION_SPEC = "aws-ec2:us-east-1";
- 
-    @CatalogConfig(label="Number of Availability Zones", priority=1)
-    public static final ConfigKey<Integer> NUM_AVAILABILITY_ZONES = ConfigKeys.newConfigKey(
-        "cassandra.cluster.numAvailabilityZones", "Number of availability zones to spread the cluster across", 3); 
-    
-    @CatalogConfig(label="Initial Cluster Size", priority=2)
-    public static final ConfigKey<Integer> CASSANDRA_CLUSTER_SIZE = ConfigKeys.newConfigKey(
-        "cassandra.cluster.initialSize", "Initial size of the Cassandra cluster", 6);      
-    
-    
-    @Override
-    public void initApp() {
-        addChild(EntitySpec.create(CassandraDatacenter.class)
-                .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn")
-                .configure(CassandraDatacenter.INITIAL_SIZE, getConfig(CASSANDRA_CLUSTER_SIZE))
-                .configure(CassandraDatacenter.ENABLE_AVAILABILITY_ZONES, true)
-                .configure(CassandraDatacenter.NUM_AVAILABILITY_ZONES, getConfig(NUM_AVAILABILITY_ZONES))
-                //See https://github.com/brooklyncentral/brooklyn/issues/973
-                //.configure(CassandraCluster.AVAILABILITY_ZONE_NAMES, ImmutableList.of("us-east-1b", "us-east-1c", "us-east-1e"))
-                .configure(CassandraDatacenter.ENDPOINT_SNITCH_NAME, "GossipingPropertyFileSnitch")
-                .configure(CassandraDatacenter.MEMBER_SPEC, EntitySpec.create(CassandraNode.class)
-                        .enricher(EnricherSpec.create(ServiceFailureDetector.class))
-                        .policy(PolicySpec.create(ServiceRestarter.class)
-                                .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, ServiceFailureDetector.ENTITY_FAILED)))
-                .policy(PolicySpec.create(ServiceReplacer.class)
-                        .configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, ServiceRestarter.ENTITY_RESTART_FAILED)));
-    }
-    
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION_SPEC);
-        
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                 .application(EntitySpec.create(StartableApplication.class, HighAvailabilityCassandraCluster.class)
-                         .displayName("Cassandra"))
-                 .webconsolePort(port)
-                 .location(location)
-                 .start();
-             
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/ResilientMongoDbApp.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/ResilientMongoDbApp.java b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/ResilientMongoDbApp.java
deleted file mode 100644
index 4acbbea..0000000
--- a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/ResilientMongoDbApp.java
+++ /dev/null
@@ -1,106 +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.demo;
-
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.catalog.Catalog;
-import brooklyn.enricher.Enrichers;
-import brooklyn.entity.Entity;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.SoftwareProcess;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.group.DynamicCluster;
-import brooklyn.entity.nosql.mongodb.MongoDBReplicaSet;
-import brooklyn.entity.nosql.mongodb.MongoDBServer;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.event.SensorEvent;
-import brooklyn.event.SensorEventListener;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.policy.ha.ServiceFailureDetector;
-import brooklyn.policy.ha.ServiceReplacer;
-import brooklyn.policy.ha.ServiceRestarter;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.collect.Lists;
-
-/**
- * Sample showing a MongoDB replica set with resilience policies attached at nodes and the cluster.
- **/
-@Catalog(name="Resilient MongoDB")
-public class ResilientMongoDbApp extends AbstractApplication implements StartableApplication {
-    
-    public static final Logger LOG = LoggerFactory.getLogger(ResilientMongoDbApp.class);
-    
-    public static final String DEFAULT_LOCATION = "named:gce-europe-west1";
-
-    @Override
-    public void initApp() {
-        MongoDBReplicaSet rs = addChild(
-                EntitySpec.create(MongoDBReplicaSet.class)
-                        .configure(MongoDBReplicaSet.INITIAL_SIZE, 3));
-        
-        initResilience(rs);
-        
-        addEnricher(Enrichers.builder()
-                .propagating(MongoDBReplicaSet.REPLICA_SET_ENDPOINTS, MongoDBServer.REPLICA_SET_PRIMARY_ENDPOINT)
-                .from(rs)
-                .build());
-    }
-    
-    /** this attaches a policy at each MongoDB node listening for ENTITY_FAILED,
-     * attempting to _restart_ the process, and 
-     * failing that attempting to _replace_ the entity (e.g. a new VM), and 
-     * failing that setting the cluster "on-fire" */
-    protected void initResilience(MongoDBReplicaSet rs) {
-        subscribe(rs, DynamicCluster.MEMBER_ADDED, new SensorEventListener<Entity>() {
-            @Override
-            public void onEvent(SensorEvent<Entity> addition) {
-                initSoftwareProcess((SoftwareProcess)addition.getValue());
-            }
-        });
-        rs.addPolicy(new ServiceReplacer(ServiceRestarter.ENTITY_RESTART_FAILED));
-    }
-
-    /** invoked whenever a new MongoDB server is added (the server may not be started yet) */
-    protected void initSoftwareProcess(SoftwareProcess p) {
-        p.addEnricher(new ServiceFailureDetector());
-        p.addPolicy(new ServiceRestarter(ServiceFailureDetector.ENTITY_FAILED));
-    }
-    
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                 .application(EntitySpec.create(StartableApplication.class, ResilientMongoDbApp.class)
-                         .displayName("Resilient MongoDB"))
-                 .webconsolePort(port)
-                 .location(location)
-                 .start();
-             
-        Entities.dumpInfo(launcher.getApplications());
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/RiakClusterExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/RiakClusterExample.java b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/RiakClusterExample.java
deleted file mode 100644
index 58b6f60..0000000
--- a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/RiakClusterExample.java
+++ /dev/null
@@ -1,76 +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.demo;
-
-import java.util.List;
-
-import brooklyn.catalog.Catalog;
-import brooklyn.catalog.CatalogConfig;
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.nosql.riak.RiakCluster;
-import brooklyn.entity.nosql.riak.RiakNode;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.policy.EnricherSpec;
-import brooklyn.policy.PolicySpec;
-import brooklyn.policy.ha.ServiceFailureDetector;
-import brooklyn.policy.ha.ServiceRestarter;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-@Catalog(name = "Riak Cluster Application", description = "Riak ring deployment blueprint")
-public class RiakClusterExample extends AbstractApplication {
-
-    public static final String DEFAULT_LOCATION_SPEC = "aws-ec2:us-east-1";
-
-    @CatalogConfig(label = "Riak Ring Size")
-    public static final ConfigKey<Integer> RIAK_RING_SIZE = ConfigKeys.newConfigKey(
-            "riak.ring.size", "Initial size of the Riak Ring", 4);
-
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port = CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION_SPEC);
-        Preconditions.checkArgument(args.isEmpty(), "Unsupported args: " + args);
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                .application(EntitySpec.create(StartableApplication.class, RiakClusterExample.class))
-                .webconsolePort(port)
-                .location(location)
-                .start();
-
-        Entities.dumpInfo(launcher.getApplications());
-    }
-
-    public void initApp() {
-        addChild(EntitySpec.create(RiakCluster.class)
-                .configure(RiakCluster.INITIAL_SIZE, getConfig(RIAK_RING_SIZE))
-                .configure(RiakCluster.MEMBER_SPEC, EntitySpec.create(RiakNode.class)
-                        .enricher(EnricherSpec.create(ServiceFailureDetector.class))
-                        .policy(PolicySpec.create(ServiceRestarter.class)
-                                .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, ServiceFailureDetector.ENTITY_FAILED))));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleCassandraCluster.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleCassandraCluster.java b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleCassandraCluster.java
deleted file mode 100644
index 94f792c..0000000
--- a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleCassandraCluster.java
+++ /dev/null
@@ -1,58 +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.demo;
-
-import java.util.List;
-
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.nosql.cassandra.CassandraDatacenter;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.collect.Lists;
-
-public class SimpleCassandraCluster extends AbstractApplication {
-
-    private static final String DEFAULT_LOCATION = "localhost";
-
-    @Override
-    public void initApp() {
-        addChild(EntitySpec.create(CassandraDatacenter.class)
-                .configure(CassandraDatacenter.INITIAL_SIZE, 1)
-                .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn"));
-    }
-    
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
-        
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                 .application(EntitySpec.create(StartableApplication.class, SimpleCassandraCluster.class)
-                         .displayName("Cassandra"))
-                         .webconsolePort(port)
-                 .location(location)
-                 .start();
-             
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleCouchDBCluster.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleCouchDBCluster.java b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleCouchDBCluster.java
deleted file mode 100644
index 6f249e2..0000000
--- a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleCouchDBCluster.java
+++ /dev/null
@@ -1,36 +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.demo;
-
-import brooklyn.entity.basic.ApplicationBuilder;
-import brooklyn.entity.nosql.couchdb.CouchDBCluster;
-import brooklyn.entity.proxying.EntitySpec;
-
-/** CouchDB cluster. */
-public class SimpleCouchDBCluster extends ApplicationBuilder {
-
-    /** Create entities. */
-    protected void doBuild() {
-        addChild(EntitySpec.create(CouchDBCluster.class)
-                .configure("initialSize", "2")
-                .configure("clusterName", "Brooklyn")
-                .configure("httpPort", "8000+"));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleMongoDBReplicaSet.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleMongoDBReplicaSet.java b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleMongoDBReplicaSet.java
deleted file mode 100644
index 5597590..0000000
--- a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleMongoDBReplicaSet.java
+++ /dev/null
@@ -1,39 +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.demo;
-
-import brooklyn.entity.basic.ApplicationBuilder;
-import brooklyn.entity.nosql.mongodb.MongoDBReplicaSet;
-import brooklyn.entity.nosql.mongodb.MongoDBServer;
-import brooklyn.entity.proxying.EntitySpec;
-
-public class SimpleMongoDBReplicaSet extends ApplicationBuilder {
-
-    protected void doBuild() {
-        addChild(EntitySpec.create(MongoDBReplicaSet.class)
-            .configure("name", "Simple MongoDB replica set")
-            .configure("initialSize", 3)
-            .configure("replicaSetName", "simple-replica-set")
-            .configure("memberSpec", EntitySpec.create(MongoDBServer.class)
-                    .configure("mongodbConfTemplateUrl", "classpath:///mongodb.conf")
-                    .configure("enableRestInterface", true)
-                    .configure("port", "27017+")));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleRedisCluster.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleRedisCluster.java b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleRedisCluster.java
deleted file mode 100644
index ed899b3..0000000
--- a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/SimpleRedisCluster.java
+++ /dev/null
@@ -1,35 +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.demo;
-
-import brooklyn.entity.basic.ApplicationBuilder;
-import brooklyn.entity.nosql.redis.RedisCluster;
-import brooklyn.entity.proxying.EntitySpec;
-
-/** Redis cluster. */
-public class SimpleRedisCluster extends ApplicationBuilder {
-
-    /** Create entities. */
-    protected void doBuild() {
-        addChild(EntitySpec.create(RedisCluster.class)
-                .configure("initialSize", "2")
-                .configure("clusterName", "Brooklyn"));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/StormSampleApp.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/StormSampleApp.java b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/StormSampleApp.java
deleted file mode 100644
index 732ccd5..0000000
--- a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/StormSampleApp.java
+++ /dev/null
@@ -1,70 +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.demo;
-
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.catalog.Catalog;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.messaging.storm.StormDeployment;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.collect.Lists;
-
-/**
- * Sample showing a storm analyticscluster
- **/
-@Catalog(name="Storm Sample App",
-description="Creates a Storm analytics cluster",
-    iconUrl="classpath://brooklyn/demo/glossy-3d-blue-web-icon.png")
-public class StormSampleApp extends AbstractApplication implements StartableApplication {
-
-    public static final Logger LOG = LoggerFactory.getLogger(StormSampleApp.class);
-
-    public static final String DEFAULT_LOCATION = "named:gce-europe-west1";
-
-    @Override
-    public void initApp() {
-        addChild(EntitySpec.create(StormDeployment.class)
-            .configure(StormDeployment.SUPERVISORS_COUNT, 2)
-            .configure(StormDeployment.ZOOKEEPERS_COUNT, 1));
-    }
-
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-            .application(EntitySpec.create(StartableApplication.class, StormSampleApp.class)
-                .displayName("Storm App"))
-                .webconsolePort(port)
-                .location(location)
-                .start();
-
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/WideAreaCassandraCluster.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/WideAreaCassandraCluster.java b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/WideAreaCassandraCluster.java
deleted file mode 100644
index 7c441ee..0000000
--- a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/WideAreaCassandraCluster.java
+++ /dev/null
@@ -1,86 +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.demo;
-
-import java.util.Arrays;
-import java.util.List;
-
-import brooklyn.catalog.Catalog;
-import brooklyn.catalog.CatalogConfig;
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.nosql.cassandra.CassandraDatacenter;
-import brooklyn.entity.nosql.cassandra.CassandraFabric;
-import brooklyn.entity.nosql.cassandra.CassandraNode;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.policy.EnricherSpec;
-import brooklyn.policy.PolicySpec;
-import brooklyn.policy.ha.ServiceFailureDetector;
-import brooklyn.policy.ha.ServiceReplacer;
-import brooklyn.policy.ha.ServiceRestarter;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.collect.Lists;
-
-@Catalog(name="Wide Area Cassandra Cluster Application", description="Deploy a Cassandra cluster across multiple geographies.")
-public class WideAreaCassandraCluster extends AbstractApplication {
-
-    public static final String DEFAULT_LOCATION_SPEC = "aws-ec2:us-east-1,rackspace-cloudservers-uk";
-    
-    @CatalogConfig(label="Initial Cluster Size (per location)", priority=2)
-    public static final ConfigKey<Integer> CASSANDRA_CLUSTER_SIZE = ConfigKeys.newConfigKey(
-        "cassandra.cluster.initialSize", "Initial size of the Cassandra clusterss", 2);      
-    
-    
-    
-    @Override
-    public void initApp() {
-        addChild(EntitySpec.create(CassandraFabric.class)
-                .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn")
-                .configure(CassandraDatacenter.INITIAL_SIZE, getConfig(CASSANDRA_CLUSTER_SIZE)) // per location
-                .configure(CassandraDatacenter.ENDPOINT_SNITCH_NAME, "brooklyn.entity.nosql.cassandra.customsnitch.MultiCloudSnitch")
-                .configure(CassandraNode.CUSTOM_SNITCH_JAR_URL, "classpath://brooklyn/entity/nosql/cassandra/cassandra-multicloud-snitch.jar")
-                .configure(CassandraFabric.MEMBER_SPEC, EntitySpec.create(CassandraDatacenter.class)
-                        .configure(CassandraDatacenter.MEMBER_SPEC, EntitySpec.create(CassandraNode.class)
-                                .enricher(EnricherSpec.create(ServiceFailureDetector.class))
-                                .policy(PolicySpec.create(ServiceRestarter.class)
-                                        .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, ServiceFailureDetector.ENTITY_FAILED)))
-                        .policy(PolicySpec.create(ServiceReplacer.class)
-                                .configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, ServiceRestarter.ENTITY_RESTART_FAILED))));
-    }
-    
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String locations = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION_SPEC);
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                 .application(EntitySpec.create(StartableApplication.class, WideAreaCassandraCluster.class)
-                         .displayName("Cassandra"))
-                 .webconsolePort(port)
-                 .locations(Arrays.asList(locations))
-                 .start();
-             
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/CumulusRDFApplication.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/CumulusRDFApplication.java b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/CumulusRDFApplication.java
new file mode 100644
index 0000000..f2ec860
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/CumulusRDFApplication.java
@@ -0,0 +1,240 @@
+/*
+ * 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.demo;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.catalog.Catalog;
+import brooklyn.catalog.CatalogConfig;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Effector;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.basic.ServiceStateLogic;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.effector.EffectorBody;
+import brooklyn.entity.effector.Effectors;
+import brooklyn.entity.java.UsesJava;
+import brooklyn.entity.java.UsesJmx;
+import brooklyn.entity.nosql.cassandra.CassandraDatacenter;
+import brooklyn.entity.nosql.cassandra.CassandraFabric;
+import brooklyn.entity.nosql.cassandra.CassandraNode;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.software.SshEffectorTasks;
+import brooklyn.entity.trait.Startable;
+import brooklyn.entity.webapp.JavaWebAppService;
+import brooklyn.entity.webapp.tomcat.TomcatServer;
+import brooklyn.event.SensorEvent;
+import brooklyn.event.SensorEventListener;
+import brooklyn.event.basic.DependentConfiguration;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.location.Location;
+import brooklyn.location.basic.PortRanges;
+import brooklyn.policy.EnricherSpec;
+import brooklyn.policy.PolicySpec;
+import brooklyn.policy.ha.ServiceFailureDetector;
+import brooklyn.policy.ha.ServiceReplacer;
+import brooklyn.policy.ha.ServiceRestarter;
+import brooklyn.util.CommandLineUtil;
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.task.DynamicTasks;
+import brooklyn.util.text.StringEscapes.JavaStringEscapes;
+import brooklyn.util.text.Strings;
+import brooklyn.util.text.TemplateProcessor;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.net.HostAndPort;
+
+/** CumulusRDF application with Cassandra cluster. */
+@Catalog(name="Cumulus RDF Application", description="CumulusRDF Application on a Tomcat server using a multi-region Cassandra fabric")
+public class CumulusRDFApplication extends AbstractApplication {
+
+    private static final Logger log = LoggerFactory.getLogger(CumulusRDFApplication.class);
+
+    @CatalogConfig(label="Cumulus Configuration File (URL)", priority=1)
+    public static final ConfigKey<String> CUMULUS_RDF_CONFIG_URL = ConfigKeys.newConfigKey(
+        "cumulus.config.url", "URL for the YAML configuration file for CumulusRDF", "classpath://cumulus.yaml");
+
+    @CatalogConfig(label="Cassandra Thrift Port", priority=1)
+    public static final ConfigKey<Integer> CASSANDRA_THRIFT_PORT = ConfigKeys.newConfigKey(
+        "cumulus.cassandra.port", "Port to contact the Cassandra cluster on", 9160);
+
+    @CatalogConfig(label="Cassandra Cluster Size", priority=1)
+    public static final ConfigKey<Integer> CASSANDRA_CLUSTER_SIZE = ConfigKeys.newConfigKey(
+        "cumulus.cassandra.cluster.size", "Initial size of the Cassandra cluster", 2);
+
+    @CatalogConfig(label="Multi-region Fabric", priority=1)
+    public static final ConfigKey<Boolean> MULTI_REGION_FABRIC = ConfigKeys.newConfigKey(
+        "cumulus.cassandra.fabric", "Deploy a multi-region Cassandra fabric", false);
+
+    // TODO Fails when given two locations
+    // public static final String DEFAULT_LOCATIONS = "[ jclouds:aws-ec2:us-east-1,jclouds:rackspace-cloudservers-uk ]";
+    public static final String DEFAULT_LOCATIONS = "jclouds:aws-ec2:us-east-1";
+
+    private Effector<Void> cumulusConfig = Effectors.effector(Void.class, "cumulusConfig")
+            .description("Configure the CumulusRDF web application")
+            .buildAbstract();
+
+    private Entity cassandra;
+    private TomcatServer webapp;
+    private HostAndPort endpoint;
+    private final Object endpointMutex = new Object();
+
+    /**
+     * Create the application entities:
+     * <ul>
+     * <li>A {@link CassandraFabric} of {@link CassandraDatacenter}s containing {@link CassandraNode}s
+     * <li>A {@link TomcatServer}
+     * </ul>
+     */
+    @Override
+    public void initApp() {
+        // Cassandra cluster
+        EntitySpec<CassandraDatacenter> clusterSpec = EntitySpec.create(CassandraDatacenter.class)
+                .configure(CassandraDatacenter.MEMBER_SPEC, EntitySpec.create(CassandraNode.class)
+                        //FIXME can probably use JMXMP_AND_RMI now, to deploy to GCE and elsewhere
+                        .configure(UsesJmx.JMX_AGENT_MODE, UsesJmx.JmxAgentModes.JMX_RMI_CUSTOM_AGENT)
+                        .configure(UsesJmx.JMX_PORT, PortRanges.fromString("11099+"))
+                        .configure(UsesJmx.RMI_REGISTRY_PORT, PortRanges.fromString("9001+"))
+                        .configure(CassandraNode.THRIFT_PORT, PortRanges.fromInteger(getConfig(CASSANDRA_THRIFT_PORT)))
+                        .enricher(EnricherSpec.create(ServiceFailureDetector.class))
+                        .policy(PolicySpec.create(ServiceRestarter.class)
+                                .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, ServiceFailureDetector.ENTITY_FAILED)))
+                .policy(PolicySpec.create(ServiceReplacer.class)
+                        .configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, ServiceRestarter.ENTITY_RESTART_FAILED));
+
+        if (getConfig(MULTI_REGION_FABRIC)) {
+            cassandra = addChild(EntitySpec.create(CassandraFabric.class)
+                    .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn")
+                    .configure(CassandraDatacenter.INITIAL_SIZE, getConfig(CASSANDRA_CLUSTER_SIZE)) // per location
+                    .configure(CassandraDatacenter.ENDPOINT_SNITCH_NAME, "brooklyn.entity.nosql.cassandra.customsnitch.MultiCloudSnitch")
+                    .configure(CassandraNode.CUSTOM_SNITCH_JAR_URL, "classpath://brooklyn/entity/nosql/cassandra/cassandra-multicloud-snitch.jar")
+                    .configure(CassandraFabric.MEMBER_SPEC, clusterSpec));
+        } else {
+            cassandra = addChild(EntitySpec.create(clusterSpec)
+                    .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn")
+                    .configure(CassandraDatacenter.INITIAL_SIZE, getConfig(CASSANDRA_CLUSTER_SIZE)));
+        }
+
+        // Tomcat web-app server
+        webapp = addChild(EntitySpec.create(TomcatServer.class)
+                .configure(UsesJmx.JMX_AGENT_MODE, UsesJmx.JmxAgentModes.JMX_RMI_CUSTOM_AGENT)
+                .configure(UsesJmx.JMX_PORT, PortRanges.fromString("11099+"))
+                .configure(UsesJmx.RMI_REGISTRY_PORT, PortRanges.fromString("9001+"))
+                .configure(JavaWebAppService.ROOT_WAR, "https://cumulusrdf.googlecode.com/svn/wiki/downloads/cumulusrdf-1.0.1.war")
+                .configure(UsesJava.JAVA_SYSPROPS, MutableMap.of("cumulusrdf.config-file", "/tmp/cumulus.yaml")));
+
+        // Add an effector to tomcat to reconfigure with a new YAML config file
+        ((EntityInternal) webapp).getMutableEntityType().addEffector(cumulusConfig, new EffectorBody<Void>() {
+            @Override
+            public Void call(ConfigBag parameters) {
+                // Process the YAML template given in the application config
+                String url = Entities.getRequiredUrlConfig(CumulusRDFApplication.this, CUMULUS_RDF_CONFIG_URL);
+                Map<String, Object> config;
+                synchronized (endpointMutex) {
+                    config = MutableMap.<String, Object>of("cassandraHostname", endpoint.getHostText(), "cassandraThriftPort", endpoint.getPort());
+                }
+                String contents = TemplateProcessor.processTemplateContents(ResourceUtils.create(CumulusRDFApplication.this).getResourceAsString(url), config);
+                // Copy the file contents to the remote machine
+                return DynamicTasks.queue(SshEffectorTasks.put("/tmp/cumulus.yaml").contents(contents)).get();
+            }
+        });
+
+        // Listen for HOSTNAME changes from the Cassandra fabric to show at least one node is available
+        subscribe(cassandra, CassandraDatacenter.HOSTNAME, new SensorEventListener<String>() {
+            @Override
+            public void onEvent(SensorEvent<String> event) {
+                if (Strings.isNonBlank(event.getValue())) {
+                    synchronized (endpointMutex) {
+                        String hostname = Entities.submit(CumulusRDFApplication.this, DependentConfiguration.attributeWhenReady(cassandra, CassandraDatacenter.HOSTNAME)).getUnchecked();
+                        Integer thriftPort = Entities.submit(CumulusRDFApplication.this, DependentConfiguration.attributeWhenReady(cassandra, CassandraDatacenter.THRIFT_PORT)).getUnchecked();
+                        HostAndPort current = HostAndPort.fromParts(hostname, thriftPort);
+
+                        // Check if the cluster access point has changed
+                        if (!current.equals(endpoint)) {
+                            log.info("Setting cluster endpoint to {}", current.toString());
+                            endpoint = current;
+
+                            // Reconfigure the CumulusRDF application and restart tomcat if necessary
+                            webapp.invoke(cumulusConfig, MutableMap.<String, Object>of());
+                            if (webapp.getAttribute(Startable.SERVICE_UP)) {
+                                webapp.restart();
+                            }
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Controls the startup locations for the webapp and the cassandra fabric.
+     *
+     * @see AbstractApplication#start(Collection)
+     */
+    @Override
+    public void start(Collection<? extends Location> locations) {
+        addLocations(locations);
+
+        // The web application only needs to run in one location, use the first
+        // TODO use a multi-region web cluster
+        Collection<? extends Location> first = MutableList.copyOf(Iterables.limit(locations, 1));
+
+        ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
+        try {
+            Entities.invokeEffector(this, cassandra, Startable.START, MutableMap.of("locations", locations)).getUnchecked();
+            Entities.invokeEffector(this, webapp, Startable.START, MutableMap.of("locations", first)).getUnchecked();
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        } finally {
+            ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING);
+        }
+        log.info("Started CumulusRDF in " + locations);
+    }
+
+
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String locations = CommandLineUtil.getCommandLineOption(args, "--locations", DEFAULT_LOCATIONS);
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .application(EntitySpec.create(StartableApplication.class, CumulusRDFApplication.class).displayName("CumulusRDF application using Cassandra"))
+                .webconsolePort(port)
+                .locations(Strings.isBlank(locations) ? ImmutableList.<String>of() : JavaStringEscapes.unwrapJsonishListIfPossible(locations))
+                .start();
+
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/HighAvailabilityCassandraCluster.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/HighAvailabilityCassandraCluster.java b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/HighAvailabilityCassandraCluster.java
new file mode 100644
index 0000000..b6800f1
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/HighAvailabilityCassandraCluster.java
@@ -0,0 +1,89 @@
+/*
+ * 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.demo;
+
+import java.util.List;
+
+import brooklyn.catalog.Catalog;
+import brooklyn.catalog.CatalogConfig;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.nosql.cassandra.CassandraDatacenter;
+import brooklyn.entity.nosql.cassandra.CassandraNode;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.policy.EnricherSpec;
+import brooklyn.policy.PolicySpec;
+import brooklyn.policy.ha.ServiceFailureDetector;
+import brooklyn.policy.ha.ServiceReplacer;
+import brooklyn.policy.ha.ServiceRestarter;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.collect.Lists;
+
+@Catalog(name="HA Cassandra Cluster Application", description="Deploy a Cassandra cluster with a High Availability architecture.")
+public class HighAvailabilityCassandraCluster extends AbstractApplication {
+
+    public static final String DEFAULT_LOCATION_SPEC = "aws-ec2:us-east-1";
+ 
+    @CatalogConfig(label="Number of Availability Zones", priority=1)
+    public static final ConfigKey<Integer> NUM_AVAILABILITY_ZONES = ConfigKeys.newConfigKey(
+        "cassandra.cluster.numAvailabilityZones", "Number of availability zones to spread the cluster across", 3); 
+    
+    @CatalogConfig(label="Initial Cluster Size", priority=2)
+    public static final ConfigKey<Integer> CASSANDRA_CLUSTER_SIZE = ConfigKeys.newConfigKey(
+        "cassandra.cluster.initialSize", "Initial size of the Cassandra cluster", 6);      
+    
+    
+    @Override
+    public void initApp() {
+        addChild(EntitySpec.create(CassandraDatacenter.class)
+                .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn")
+                .configure(CassandraDatacenter.INITIAL_SIZE, getConfig(CASSANDRA_CLUSTER_SIZE))
+                .configure(CassandraDatacenter.ENABLE_AVAILABILITY_ZONES, true)
+                .configure(CassandraDatacenter.NUM_AVAILABILITY_ZONES, getConfig(NUM_AVAILABILITY_ZONES))
+                //See https://github.com/brooklyncentral/brooklyn/issues/973
+                //.configure(CassandraCluster.AVAILABILITY_ZONE_NAMES, ImmutableList.of("us-east-1b", "us-east-1c", "us-east-1e"))
+                .configure(CassandraDatacenter.ENDPOINT_SNITCH_NAME, "GossipingPropertyFileSnitch")
+                .configure(CassandraDatacenter.MEMBER_SPEC, EntitySpec.create(CassandraNode.class)
+                        .enricher(EnricherSpec.create(ServiceFailureDetector.class))
+                        .policy(PolicySpec.create(ServiceRestarter.class)
+                                .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, ServiceFailureDetector.ENTITY_FAILED)))
+                .policy(PolicySpec.create(ServiceReplacer.class)
+                        .configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, ServiceRestarter.ENTITY_RESTART_FAILED)));
+    }
+    
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION_SPEC);
+        
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                 .application(EntitySpec.create(StartableApplication.class, HighAvailabilityCassandraCluster.class)
+                         .displayName("Cassandra"))
+                 .webconsolePort(port)
+                 .location(location)
+                 .start();
+             
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/ResilientMongoDbApp.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/ResilientMongoDbApp.java b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/ResilientMongoDbApp.java
new file mode 100644
index 0000000..d98a11c
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/ResilientMongoDbApp.java
@@ -0,0 +1,106 @@
+/*
+ * 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.demo;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.catalog.Catalog;
+import brooklyn.enricher.Enrichers;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.SoftwareProcess;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.group.DynamicCluster;
+import brooklyn.entity.nosql.mongodb.MongoDBReplicaSet;
+import brooklyn.entity.nosql.mongodb.MongoDBServer;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.event.SensorEvent;
+import brooklyn.event.SensorEventListener;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.policy.ha.ServiceFailureDetector;
+import brooklyn.policy.ha.ServiceReplacer;
+import brooklyn.policy.ha.ServiceRestarter;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Sample showing a MongoDB replica set with resilience policies attached at nodes and the cluster.
+ **/
+@Catalog(name="Resilient MongoDB")
+public class ResilientMongoDbApp extends AbstractApplication implements StartableApplication {
+    
+    public static final Logger LOG = LoggerFactory.getLogger(ResilientMongoDbApp.class);
+    
+    public static final String DEFAULT_LOCATION = "named:gce-europe-west1";
+
+    @Override
+    public void initApp() {
+        MongoDBReplicaSet rs = addChild(
+                EntitySpec.create(MongoDBReplicaSet.class)
+                        .configure(MongoDBReplicaSet.INITIAL_SIZE, 3));
+        
+        initResilience(rs);
+        
+        addEnricher(Enrichers.builder()
+                .propagating(MongoDBReplicaSet.REPLICA_SET_ENDPOINTS, MongoDBServer.REPLICA_SET_PRIMARY_ENDPOINT)
+                .from(rs)
+                .build());
+    }
+    
+    /** this attaches a policy at each MongoDB node listening for ENTITY_FAILED,
+     * attempting to _restart_ the process, and 
+     * failing that attempting to _replace_ the entity (e.g. a new VM), and 
+     * failing that setting the cluster "on-fire" */
+    protected void initResilience(MongoDBReplicaSet rs) {
+        subscribe(rs, DynamicCluster.MEMBER_ADDED, new SensorEventListener<Entity>() {
+            @Override
+            public void onEvent(SensorEvent<Entity> addition) {
+                initSoftwareProcess((SoftwareProcess)addition.getValue());
+            }
+        });
+        rs.addPolicy(new ServiceReplacer(ServiceRestarter.ENTITY_RESTART_FAILED));
+    }
+
+    /** invoked whenever a new MongoDB server is added (the server may not be started yet) */
+    protected void initSoftwareProcess(SoftwareProcess p) {
+        p.addEnricher(new ServiceFailureDetector());
+        p.addPolicy(new ServiceRestarter(ServiceFailureDetector.ENTITY_FAILED));
+    }
+    
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                 .application(EntitySpec.create(StartableApplication.class, ResilientMongoDbApp.class)
+                         .displayName("Resilient MongoDB"))
+                 .webconsolePort(port)
+                 .location(location)
+                 .start();
+             
+        Entities.dumpInfo(launcher.getApplications());
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/RiakClusterExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/RiakClusterExample.java b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/RiakClusterExample.java
new file mode 100644
index 0000000..672aba3
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/RiakClusterExample.java
@@ -0,0 +1,76 @@
+/*
+ * 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.demo;
+
+import java.util.List;
+
+import brooklyn.catalog.Catalog;
+import brooklyn.catalog.CatalogConfig;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.nosql.riak.RiakCluster;
+import brooklyn.entity.nosql.riak.RiakNode;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.policy.EnricherSpec;
+import brooklyn.policy.PolicySpec;
+import brooklyn.policy.ha.ServiceFailureDetector;
+import brooklyn.policy.ha.ServiceRestarter;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+@Catalog(name = "Riak Cluster Application", description = "Riak ring deployment blueprint")
+public class RiakClusterExample extends AbstractApplication {
+
+    public static final String DEFAULT_LOCATION_SPEC = "aws-ec2:us-east-1";
+
+    @CatalogConfig(label = "Riak Ring Size")
+    public static final ConfigKey<Integer> RIAK_RING_SIZE = ConfigKeys.newConfigKey(
+            "riak.ring.size", "Initial size of the Riak Ring", 4);
+
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port = CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION_SPEC);
+        Preconditions.checkArgument(args.isEmpty(), "Unsupported args: " + args);
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .application(EntitySpec.create(StartableApplication.class, RiakClusterExample.class))
+                .webconsolePort(port)
+                .location(location)
+                .start();
+
+        Entities.dumpInfo(launcher.getApplications());
+    }
+
+    public void initApp() {
+        addChild(EntitySpec.create(RiakCluster.class)
+                .configure(RiakCluster.INITIAL_SIZE, getConfig(RIAK_RING_SIZE))
+                .configure(RiakCluster.MEMBER_SPEC, EntitySpec.create(RiakNode.class)
+                        .enricher(EnricherSpec.create(ServiceFailureDetector.class))
+                        .policy(PolicySpec.create(ServiceRestarter.class)
+                                .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, ServiceFailureDetector.ENTITY_FAILED))));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleCassandraCluster.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleCassandraCluster.java b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleCassandraCluster.java
new file mode 100644
index 0000000..2ba6ba3
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleCassandraCluster.java
@@ -0,0 +1,58 @@
+/*
+ * 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.demo;
+
+import java.util.List;
+
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.nosql.cassandra.CassandraDatacenter;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.collect.Lists;
+
+public class SimpleCassandraCluster extends AbstractApplication {
+
+    private static final String DEFAULT_LOCATION = "localhost";
+
+    @Override
+    public void initApp() {
+        addChild(EntitySpec.create(CassandraDatacenter.class)
+                .configure(CassandraDatacenter.INITIAL_SIZE, 1)
+                .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn"));
+    }
+    
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
+        
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                 .application(EntitySpec.create(StartableApplication.class, SimpleCassandraCluster.class)
+                         .displayName("Cassandra"))
+                         .webconsolePort(port)
+                 .location(location)
+                 .start();
+             
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleCouchDBCluster.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleCouchDBCluster.java b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleCouchDBCluster.java
new file mode 100644
index 0000000..0919c24
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleCouchDBCluster.java
@@ -0,0 +1,36 @@
+/*
+ * 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.demo;
+
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.nosql.couchdb.CouchDBCluster;
+import brooklyn.entity.proxying.EntitySpec;
+
+/** CouchDB cluster. */
+public class SimpleCouchDBCluster extends ApplicationBuilder {
+
+    /** Create entities. */
+    protected void doBuild() {
+        addChild(EntitySpec.create(CouchDBCluster.class)
+                .configure("initialSize", "2")
+                .configure("clusterName", "Brooklyn")
+                .configure("httpPort", "8000+"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleMongoDBReplicaSet.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleMongoDBReplicaSet.java b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleMongoDBReplicaSet.java
new file mode 100644
index 0000000..249431c
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleMongoDBReplicaSet.java
@@ -0,0 +1,39 @@
+/*
+ * 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.demo;
+
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.nosql.mongodb.MongoDBReplicaSet;
+import brooklyn.entity.nosql.mongodb.MongoDBServer;
+import brooklyn.entity.proxying.EntitySpec;
+
+public class SimpleMongoDBReplicaSet extends ApplicationBuilder {
+
+    protected void doBuild() {
+        addChild(EntitySpec.create(MongoDBReplicaSet.class)
+            .configure("name", "Simple MongoDB replica set")
+            .configure("initialSize", 3)
+            .configure("replicaSetName", "simple-replica-set")
+            .configure("memberSpec", EntitySpec.create(MongoDBServer.class)
+                    .configure("mongodbConfTemplateUrl", "classpath:///mongodb.conf")
+                    .configure("enableRestInterface", true)
+                    .configure("port", "27017+")));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleRedisCluster.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleRedisCluster.java b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleRedisCluster.java
new file mode 100644
index 0000000..96268c3
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/SimpleRedisCluster.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.demo;
+
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.nosql.redis.RedisCluster;
+import brooklyn.entity.proxying.EntitySpec;
+
+/** Redis cluster. */
+public class SimpleRedisCluster extends ApplicationBuilder {
+
+    /** Create entities. */
+    protected void doBuild() {
+        addChild(EntitySpec.create(RedisCluster.class)
+                .configure("initialSize", "2")
+                .configure("clusterName", "Brooklyn"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/StormSampleApp.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/StormSampleApp.java b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/StormSampleApp.java
new file mode 100644
index 0000000..37d0ec4
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/StormSampleApp.java
@@ -0,0 +1,70 @@
+/*
+ * 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.demo;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.catalog.Catalog;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.messaging.storm.StormDeployment;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Sample showing a storm analyticscluster
+ **/
+@Catalog(name="Storm Sample App",
+description="Creates a Storm analytics cluster",
+    iconUrl="classpath://brooklyn/demo/glossy-3d-blue-web-icon.png")
+public class StormSampleApp extends AbstractApplication implements StartableApplication {
+
+    public static final Logger LOG = LoggerFactory.getLogger(StormSampleApp.class);
+
+    public static final String DEFAULT_LOCATION = "named:gce-europe-west1";
+
+    @Override
+    public void initApp() {
+        addChild(EntitySpec.create(StormDeployment.class)
+            .configure(StormDeployment.SUPERVISORS_COUNT, 2)
+            .configure(StormDeployment.ZOOKEEPERS_COUNT, 1));
+    }
+
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+            .application(EntitySpec.create(StartableApplication.class, StormSampleApp.class)
+                .displayName("Storm App"))
+                .webconsolePort(port)
+                .location(location)
+                .start();
+
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}


[04/12] incubator-brooklyn git commit: Export org.apache.brooklyn.* in OSGi bundles

Posted by al...@apache.org.
Export org.apache.brooklyn.* in OSGi bundles

In preparation for renaming the base package to org.apache.brooklyn.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/0d7c48c2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/0d7c48c2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/0d7c48c2

Branch: refs/heads/master
Commit: 0d7c48c2d6346d2330d4ff3dd9c987cabd0cd657
Parents: 726bebc
Author: Ciprian Ciubotariu <ch...@gmx.net>
Authored: Tue Jul 14 18:32:40 2015 +0300
Committer: Ciprian Ciubotariu <ch...@gmx.net>
Committed: Wed Jul 15 16:56:03 2015 +0300

----------------------------------------------------------------------
 parent/pom.xml                  | 2 +-
 usage/downstream-parent/pom.xml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0d7c48c2/parent/pom.xml
----------------------------------------------------------------------
diff --git a/parent/pom.xml b/parent/pom.xml
index ee23f9d..c0080d3 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -1375,7 +1375,7 @@
                                     In time we should minimize our export lists to
                                     what is really needed.
                                 -->
-                                <Export-Package>brooklyn.*</Export-Package>
+                                <Export-Package>brooklyn.*,org.apache.brooklyn.*</Export-Package>
                                 <Implementation-SHA-1>${buildNumber}</Implementation-SHA-1>
                                 <Implementation-Branch>${scmBranch}</Implementation-Branch>
                             </instructions>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0d7c48c2/usage/downstream-parent/pom.xml
----------------------------------------------------------------------
diff --git a/usage/downstream-parent/pom.xml b/usage/downstream-parent/pom.xml
index 8d544fc..2dea7ce 100644
--- a/usage/downstream-parent/pom.xml
+++ b/usage/downstream-parent/pom.xml
@@ -454,7 +454,7 @@
                     In time we should minimize our export lists to
                     what is really needed.
                 -->
-                <Export-Package>brooklyn.*</Export-Package>
+                <Export-Package>brooklyn.*,org.apache.brooklyn.*</Export-Package>
                 <!-- Brooklyn-Feature prefix triggers inclusion of the project's metadata in the server's features list. -->
                 <Brooklyn-Feature-Name>${project.name}</Brooklyn-Feature-Name>
               </instructions>


[06/12] incubator-brooklyn git commit: brooklyn-example-global-web-fabric: add org.apache package prefix

Posted by al...@apache.org.
brooklyn-example-global-web-fabric: add org.apache package prefix


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/59cc05d5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/59cc05d5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/59cc05d5

Branch: refs/heads/master
Commit: 59cc05d598bc803e0b922a09753b4a22bfad91bf
Parents: f2792fa
Author: Ciprian Ciubotariu <ch...@gmx.net>
Authored: Wed Jul 15 17:55:45 2015 +0300
Committer: Ciprian Ciubotariu <ch...@gmx.net>
Committed: Wed Jul 15 18:47:01 2015 +0300

----------------------------------------------------------------------
 .../brooklyn/demo/GlobalWebFabricExample.java   | 119 -------------------
 .../src/main/java/brooklyn/demo/ReadMe.java     |  28 -----
 .../brooklyn/demo/GlobalWebFabricExample.java   | 119 +++++++++++++++++++
 .../java/org/apache/brooklyn/demo/ReadMe.java   |  28 +++++
 4 files changed, 147 insertions(+), 147 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/59cc05d5/examples/global-web-fabric/src/main/java/brooklyn/demo/GlobalWebFabricExample.java
----------------------------------------------------------------------
diff --git a/examples/global-web-fabric/src/main/java/brooklyn/demo/GlobalWebFabricExample.java b/examples/global-web-fabric/src/main/java/brooklyn/demo/GlobalWebFabricExample.java
deleted file mode 100644
index cf4fa24..0000000
--- a/examples/global-web-fabric/src/main/java/brooklyn/demo/GlobalWebFabricExample.java
+++ /dev/null
@@ -1,119 +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.demo;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.catalog.Catalog;
-import brooklyn.catalog.CatalogConfig;
-import brooklyn.config.ConfigKey;
-import brooklyn.config.StringConfigMap;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.dns.geoscaling.GeoscalingDnsService;
-import brooklyn.entity.group.DynamicRegionsFabric;
-import brooklyn.entity.proxy.AbstractController;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.webapp.ElasticJavaWebAppService;
-import brooklyn.entity.webapp.JavaWebAppService;
-import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.location.basic.PortRanges;
-import brooklyn.util.BrooklynMavenArtifacts;
-import brooklyn.util.CommandLineUtil;
-import brooklyn.util.ResourceUtils;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-@Catalog(name="Global Web Fabric",
-description="Deploys a WAR to multiple clusters, showing how Brooklyn fabrics work",
-iconUrl="classpath://brooklyn/demo/glossy-3d-blue-web-icon.png")
-public class GlobalWebFabricExample extends AbstractApplication {
-
-    public static final Logger log = LoggerFactory.getLogger(GlobalWebFabricExample.class);
-    
-    static final List<String> DEFAULT_LOCATIONS = ImmutableList.of(
-            "aws-ec2:eu-west-1",
-            "aws-ec2:ap-southeast-1",
-            "aws-ec2:us-west-1" );
-    
-    public static final String DEFAULT_WAR_PATH = ResourceUtils.create(GlobalWebFabricExample.class)
-        // take this war, from the classpath, or via maven if not on the classpath
-        .firstAvailableUrl(
-                "classpath://hello-world-webapp.war",
-                BrooklynMavenArtifacts.localUrl("example", "brooklyn-example-hello-world-webapp", "war"))
-        .or("classpath://hello-world-webapp.war");
-
-    @CatalogConfig(label="WAR (URL)", priority=2)
-    public static final ConfigKey<String> WAR_PATH = ConfigKeys.newConfigKey(
-        "app.war", "URL to the application archive which should be deployed", 
-        DEFAULT_WAR_PATH);    
-
-    // load-balancer instances must run on some port to work with GeoDNS, port 80 to work without special, so make that default
-    // (but included here in case it runs on a different port on all machines, or use a range to work with multiple localhosts)
-    @CatalogConfig(label="Proxy server HTTP port")
-    public static final PortAttributeSensorAndConfigKey PROXY_HTTP_PORT =
-        new PortAttributeSensorAndConfigKey(AbstractController.PROXY_HTTP_PORT, PortRanges.fromInteger(80));
-    
-    @Override
-    public void initApp() {
-        StringConfigMap config = getManagementContext().getConfig();
-        
-        GeoscalingDnsService geoDns = addChild(EntitySpec.create(GeoscalingDnsService.class)
-                .displayName("GeoScaling DNS")
-                .configure("username", checkNotNull(config.getFirst("brooklyn.geoscaling.username"), "username"))
-                .configure("password", checkNotNull(config.getFirst("brooklyn.geoscaling.password"), "password"))
-                .configure("primaryDomainName", checkNotNull(config.getFirst("brooklyn.geoscaling.primaryDomain"), "primaryDomain")) 
-                .configure("smartSubdomainName", "brooklyn"));
-        
-        DynamicRegionsFabric webFabric = addChild(EntitySpec.create(DynamicRegionsFabric.class)
-                .displayName("Web Fabric")
-                .configure(DynamicRegionsFabric.FACTORY, new ElasticJavaWebAppService.Factory())
-                
-                //specify the WAR file to use
-                .configure(JavaWebAppService.ROOT_WAR, Entities.getRequiredUrlConfig(this, WAR_PATH)) );
-
-        //tell GeoDNS what to monitor
-        geoDns.setTargetEntityProvider(webFabric);
-    }
-
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String locations = CommandLineUtil.getCommandLineOption(args, "--locations", Joiner.on(",").join(DEFAULT_LOCATIONS));
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                .application(EntitySpec.create(StartableApplication.class, GlobalWebFabricExample.class).displayName("Brooklyn Global Web Fabric Example"))
-                .webconsolePort(port)
-                .locations(Arrays.asList(locations))
-                .start();
-         
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/59cc05d5/examples/global-web-fabric/src/main/java/brooklyn/demo/ReadMe.java
----------------------------------------------------------------------
diff --git a/examples/global-web-fabric/src/main/java/brooklyn/demo/ReadMe.java b/examples/global-web-fabric/src/main/java/brooklyn/demo/ReadMe.java
deleted file mode 100644
index f5d6b74..0000000
--- a/examples/global-web-fabric/src/main/java/brooklyn/demo/ReadMe.java
+++ /dev/null
@@ -1,28 +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.demo;
-
-public class ReadMe {
-
-/* This class exists only so that javadoc is generated and artifacts can be staged at sonatype for deployment to maven central.
- * See: https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide
- *
- * (The actual examples are in other classes in this package / project.) */
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/59cc05d5/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/GlobalWebFabricExample.java
----------------------------------------------------------------------
diff --git a/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/GlobalWebFabricExample.java b/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/GlobalWebFabricExample.java
new file mode 100644
index 0000000..9f7a83a
--- /dev/null
+++ b/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/GlobalWebFabricExample.java
@@ -0,0 +1,119 @@
+/*
+ * 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.demo;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.catalog.Catalog;
+import brooklyn.catalog.CatalogConfig;
+import brooklyn.config.ConfigKey;
+import brooklyn.config.StringConfigMap;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.dns.geoscaling.GeoscalingDnsService;
+import brooklyn.entity.group.DynamicRegionsFabric;
+import brooklyn.entity.proxy.AbstractController;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.webapp.ElasticJavaWebAppService;
+import brooklyn.entity.webapp.JavaWebAppService;
+import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.location.basic.PortRanges;
+import brooklyn.util.BrooklynMavenArtifacts;
+import brooklyn.util.CommandLineUtil;
+import brooklyn.util.ResourceUtils;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+@Catalog(name="Global Web Fabric",
+description="Deploys a WAR to multiple clusters, showing how Brooklyn fabrics work",
+iconUrl="classpath://brooklyn/demo/glossy-3d-blue-web-icon.png")
+public class GlobalWebFabricExample extends AbstractApplication {
+
+    public static final Logger log = LoggerFactory.getLogger(GlobalWebFabricExample.class);
+    
+    static final List<String> DEFAULT_LOCATIONS = ImmutableList.of(
+            "aws-ec2:eu-west-1",
+            "aws-ec2:ap-southeast-1",
+            "aws-ec2:us-west-1" );
+    
+    public static final String DEFAULT_WAR_PATH = ResourceUtils.create(GlobalWebFabricExample.class)
+        // take this war, from the classpath, or via maven if not on the classpath
+        .firstAvailableUrl(
+                "classpath://hello-world-webapp.war",
+                BrooklynMavenArtifacts.localUrl("example", "brooklyn-example-hello-world-webapp", "war"))
+        .or("classpath://hello-world-webapp.war");
+
+    @CatalogConfig(label="WAR (URL)", priority=2)
+    public static final ConfigKey<String> WAR_PATH = ConfigKeys.newConfigKey(
+        "app.war", "URL to the application archive which should be deployed", 
+        DEFAULT_WAR_PATH);    
+
+    // load-balancer instances must run on some port to work with GeoDNS, port 80 to work without special, so make that default
+    // (but included here in case it runs on a different port on all machines, or use a range to work with multiple localhosts)
+    @CatalogConfig(label="Proxy server HTTP port")
+    public static final PortAttributeSensorAndConfigKey PROXY_HTTP_PORT =
+        new PortAttributeSensorAndConfigKey(AbstractController.PROXY_HTTP_PORT, PortRanges.fromInteger(80));
+    
+    @Override
+    public void initApp() {
+        StringConfigMap config = getManagementContext().getConfig();
+        
+        GeoscalingDnsService geoDns = addChild(EntitySpec.create(GeoscalingDnsService.class)
+                .displayName("GeoScaling DNS")
+                .configure("username", checkNotNull(config.getFirst("brooklyn.geoscaling.username"), "username"))
+                .configure("password", checkNotNull(config.getFirst("brooklyn.geoscaling.password"), "password"))
+                .configure("primaryDomainName", checkNotNull(config.getFirst("brooklyn.geoscaling.primaryDomain"), "primaryDomain")) 
+                .configure("smartSubdomainName", "brooklyn"));
+        
+        DynamicRegionsFabric webFabric = addChild(EntitySpec.create(DynamicRegionsFabric.class)
+                .displayName("Web Fabric")
+                .configure(DynamicRegionsFabric.FACTORY, new ElasticJavaWebAppService.Factory())
+                
+                //specify the WAR file to use
+                .configure(JavaWebAppService.ROOT_WAR, Entities.getRequiredUrlConfig(this, WAR_PATH)) );
+
+        //tell GeoDNS what to monitor
+        geoDns.setTargetEntityProvider(webFabric);
+    }
+
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String locations = CommandLineUtil.getCommandLineOption(args, "--locations", Joiner.on(",").join(DEFAULT_LOCATIONS));
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .application(EntitySpec.create(StartableApplication.class, GlobalWebFabricExample.class).displayName("Brooklyn Global Web Fabric Example"))
+                .webconsolePort(port)
+                .locations(Arrays.asList(locations))
+                .start();
+         
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/59cc05d5/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/ReadMe.java
----------------------------------------------------------------------
diff --git a/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/ReadMe.java b/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/ReadMe.java
new file mode 100644
index 0000000..6858630
--- /dev/null
+++ b/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/ReadMe.java
@@ -0,0 +1,28 @@
+/*
+ * 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.demo;
+
+public class ReadMe {
+
+/* This class exists only so that javadoc is generated and artifacts can be staged at sonatype for deployment to maven central.
+ * See: https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide
+ *
+ * (The actual examples are in other classes in this package / project.) */
+
+}


[12/12] incubator-brooklyn git commit: This closes #748

Posted by al...@apache.org.
This closes #748


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/12625d82
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/12625d82
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/12625d82

Branch: refs/heads/master
Commit: 12625d82c09fae3334cbc5c4105031fb875d7760
Parents: 726bebc 9b8bde0
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jul 16 00:09:28 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Jul 16 00:09:28 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/demo/GlobalWebFabricExample.java   | 119 -------
 .../src/main/java/brooklyn/demo/ReadMe.java     |  28 --
 .../brooklyn/demo/GlobalWebFabricExample.java   | 119 +++++++
 .../java/org/apache/brooklyn/demo/ReadMe.java   |  28 ++
 .../java/brooklyn/demo/KafkaClusterExample.java |  58 ----
 .../src/main/java/brooklyn/demo/Publish.java    |  71 ----
 .../demo/StandaloneQpidBrokerExample.java       |  73 ----
 .../src/main/java/brooklyn/demo/Subscribe.java  |  76 -----
 .../brooklyn/demo/KafkaClusterExample.java      |  58 ++++
 .../java/org/apache/brooklyn/demo/Publish.java  |  71 ++++
 .../demo/StandaloneQpidBrokerExample.java       |  73 ++++
 .../org/apache/brooklyn/demo/Subscribe.java     |  76 +++++
 .../brooklyn/demo/CumulusRDFApplication.java    | 240 --------------
 .../demo/HighAvailabilityCassandraCluster.java  |  89 -----
 .../java/brooklyn/demo/ResilientMongoDbApp.java | 106 ------
 .../java/brooklyn/demo/RiakClusterExample.java  |  76 -----
 .../brooklyn/demo/SimpleCassandraCluster.java   |  58 ----
 .../brooklyn/demo/SimpleCouchDBCluster.java     |  36 --
 .../brooklyn/demo/SimpleMongoDBReplicaSet.java  |  39 ---
 .../java/brooklyn/demo/SimpleRedisCluster.java  |  35 --
 .../main/java/brooklyn/demo/StormSampleApp.java |  70 ----
 .../brooklyn/demo/WideAreaCassandraCluster.java |  86 -----
 .../brooklyn/demo/CumulusRDFApplication.java    | 240 ++++++++++++++
 .../demo/HighAvailabilityCassandraCluster.java  |  89 +++++
 .../brooklyn/demo/ResilientMongoDbApp.java      | 106 ++++++
 .../brooklyn/demo/RiakClusterExample.java       |  76 +++++
 .../brooklyn/demo/SimpleCassandraCluster.java   |  58 ++++
 .../brooklyn/demo/SimpleCouchDBCluster.java     |  36 ++
 .../brooklyn/demo/SimpleMongoDBReplicaSet.java  |  39 +++
 .../brooklyn/demo/SimpleRedisCluster.java       |  35 ++
 .../apache/brooklyn/demo/StormSampleApp.java    |  70 ++++
 .../brooklyn/demo/WideAreaCassandraCluster.java |  86 +++++
 .../brooklyn/demo/ha-cassandra-cluster.yaml     |  45 ---
 .../brooklyn/demo/simple-cassandra-cluster.yaml |  28 --
 .../demo/wide-area-cassandra-cluster.yaml       |  41 ---
 .../brooklyn/demo/ha-cassandra-cluster.yaml     |  45 +++
 .../brooklyn/demo/simple-cassandra-cluster.yaml |  28 ++
 .../demo/wide-area-cassandra-cluster.yaml       |  41 +++
 .../src/main/assembly/scripts/start.sh          |   2 +-
 .../brooklyn/demo/NodeJsTodoApplication.java    |  59 ----
 .../brooklyn/demo/SingleWebServerExample.java   |  67 ----
 .../demo/WebClusterDatabaseExample.java         | 123 -------
 .../demo/WebClusterDatabaseExampleApp.java      | 175 ----------
 .../demo/WebClusterDatabaseExampleGroovy.groovy |  93 ------
 .../java/brooklyn/demo/WebClusterExample.java   |  96 ------
 .../brooklyn/demo/NodeJsTodoApplication.java    |  59 ++++
 .../brooklyn/demo/SingleWebServerExample.java   |  67 ++++
 .../demo/WebClusterDatabaseExample.java         | 123 +++++++
 .../demo/WebClusterDatabaseExampleApp.java      | 175 ++++++++++
 .../demo/WebClusterDatabaseExampleGroovy.groovy |  93 ++++++
 .../apache/brooklyn/demo/WebClusterExample.java |  96 ++++++
 .../brooklyn/demo/glossy-3d-blue-web-icon.png   | Bin 46490 -> 0 bytes
 .../brooklyn/demo/nodejs-riak-todo.yaml         |  46 ---
 .../resources/brooklyn/demo/nodejs-todo.yaml    |  53 ---
 .../src/main/resources/logback-custom.xml       |   2 +-
 .../brooklyn/demo/glossy-3d-blue-web-icon.png   | Bin 0 -> 46490 bytes
 .../apache/brooklyn/demo/nodejs-riak-todo.yaml  |  46 +++
 .../org/apache/brooklyn/demo/nodejs-todo.yaml   |  53 +++
 ...lusterDatabaseExampleAppIntegrationTest.java | 205 ------------
 ...lusterDatabaseExampleAppIntegrationTest.java | 206 ++++++++++++
 parent/pom.xml                                  |   2 +-
 usage/downstream-parent/pom.xml                 |   2 +-
 .../qa/load/SimulatedJBoss7ServerImpl.java      | 240 --------------
 .../qa/load/SimulatedMySqlNodeImpl.java         | 183 -----------
 .../qa/load/SimulatedNginxControllerImpl.java   | 196 -----------
 .../brooklyn/qa/load/SimulatedTheeTierApp.java  | 140 --------
 .../java/brooklyn/qa/longevity/Monitor.java     | 261 ---------------
 .../brooklyn/qa/longevity/MonitorListener.java  |  35 --
 .../brooklyn/qa/longevity/MonitorPrefs.java     |  54 ---
 .../brooklyn/qa/longevity/MonitorUtils.java     | 329 -------------------
 .../brooklyn/qa/longevity/StatusRecorder.java   | 130 --------
 .../qa/load/SimulatedJBoss7ServerImpl.java      | 240 ++++++++++++++
 .../qa/load/SimulatedMySqlNodeImpl.java         | 183 +++++++++++
 .../qa/load/SimulatedNginxControllerImpl.java   | 196 +++++++++++
 .../brooklyn/qa/load/SimulatedTheeTierApp.java  | 140 ++++++++
 .../apache/brooklyn/qa/longevity/Monitor.java   | 261 +++++++++++++++
 .../brooklyn/qa/longevity/MonitorListener.java  |  35 ++
 .../brooklyn/qa/longevity/MonitorPrefs.java     |  54 +++
 .../brooklyn/qa/longevity/MonitorUtils.java     | 329 +++++++++++++++++++
 .../brooklyn/qa/longevity/StatusRecorder.java   | 130 ++++++++
 usage/qa/src/main/resources/hello-world.war     | Bin 14693 -> 15066 bytes
 .../test/java/brooklyn/qa/load/LoadTest.java    | 243 --------------
 .../brooklyn/qa/longevity/MonitorUtilsTest.java | 165 ----------
 .../webcluster/SinusoidalLoadGenerator.java     |  90 -----
 .../qa/longevity/webcluster/WebClusterApp.java  | 101 ------
 .../org/apache/brooklyn/qa/load/LoadTest.java   | 244 ++++++++++++++
 .../brooklyn/qa/longevity/MonitorUtilsTest.java | 166 ++++++++++
 .../webcluster/SinusoidalLoadGenerator.java     |  90 +++++
 .../qa/longevity/webcluster/WebClusterApp.java  | 101 ++++++
 89 files changed, 4465 insertions(+), 4462 deletions(-)
----------------------------------------------------------------------



[07/12] incubator-brooklyn git commit: brooklyn-example-simple-web-cluster: add org.apache package prefix

Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/test/java/org/apache/brooklyn/demo/RebindWebClusterDatabaseExampleAppIntegrationTest.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/test/java/org/apache/brooklyn/demo/RebindWebClusterDatabaseExampleAppIntegrationTest.java b/examples/simple-web-cluster/src/test/java/org/apache/brooklyn/demo/RebindWebClusterDatabaseExampleAppIntegrationTest.java
new file mode 100644
index 0000000..f032d95
--- /dev/null
+++ b/examples/simple-web-cluster/src/test/java/org/apache/brooklyn/demo/RebindWebClusterDatabaseExampleAppIntegrationTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.demo;
+
+import org.apache.brooklyn.demo.WebClusterDatabaseExampleApp;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+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.enricher.HttpLatencyDetector;
+import brooklyn.enricher.basic.Propagator;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.database.mysql.MySqlNode;
+import brooklyn.entity.group.DynamicCluster;
+import brooklyn.entity.java.JavaEntityMethods;
+import brooklyn.entity.proxy.nginx.NginxController;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.rebind.RebindOptions;
+import brooklyn.entity.rebind.RebindTestFixture;
+import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
+import brooklyn.entity.webapp.DynamicWebAppCluster;
+import brooklyn.entity.webapp.tomcat.Tomcat8Server;
+import brooklyn.location.Location;
+import brooklyn.policy.Enricher;
+import brooklyn.policy.autoscaling.AutoScalerPolicy;
+import brooklyn.test.Asserts;
+import brooklyn.test.EntityTestUtils;
+import brooklyn.test.HttpTestUtils;
+import brooklyn.test.WebAppMonitor;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+
+public class RebindWebClusterDatabaseExampleAppIntegrationTest extends RebindTestFixture<StartableApplication> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RebindWebClusterDatabaseExampleAppIntegrationTest.class);
+
+    private Location origLoc;
+    private List<WebAppMonitor> webAppMonitors = new CopyOnWriteArrayList<WebAppMonitor>();
+    private ExecutorService executor;
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        origLoc = origManagementContext.getLocationRegistry().resolve("localhost");
+        executor = Executors.newCachedThreadPool();
+        webAppMonitors.clear();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        for (WebAppMonitor monitor : webAppMonitors) {
+            monitor.terminate();
+        }
+        if (executor != null) executor.shutdownNow();
+        super.tearDown();
+    }
+    
+    @Override
+    protected StartableApplication createApp() {
+        StartableApplication result = origManagementContext.getEntityManager().createEntity(EntitySpec.create(StartableApplication.class)
+                .impl(WebClusterDatabaseExampleApp.class)
+                .configure(DynamicCluster.INITIAL_SIZE, 2));
+        Entities.startManagement(result, origManagementContext);
+        return result;
+    }
+    
+    private WebAppMonitor newWebAppMonitor(String url, int expectedResponseCode) {
+        WebAppMonitor monitor = new WebAppMonitor(url)
+//              .delayMillis(0) FIXME Re-enable to fast polling
+                .expectedResponseCode(expectedResponseCode)
+                .logFailures(LOG);
+        webAppMonitors.add(monitor);
+        executor.execute(monitor);
+        return monitor;
+    }
+    
+    @Test(groups="Integration")
+    public void testRestoresSimpleApp() throws Exception {
+        origApp.start(ImmutableList.of(origLoc));
+        
+        assertAppFunctional(origApp);
+        
+        String clusterUrl = checkNotNull(origApp.getAttribute(WebClusterDatabaseExampleApp.ROOT_URL), "cluster url");
+        WebAppMonitor monitor = newWebAppMonitor(clusterUrl, 200);
+        
+        newApp = rebind(RebindOptions.create().terminateOrigManagementContext(true));
+        assertAppFunctional(newApp);
+
+        // expect no failures during rebind
+        monitor.assertNoFailures("hitting nginx url");
+        monitor.terminate();
+    }
+    
+    private void assertAppFunctional(StartableApplication app) throws Exception {
+        // expect standard config to (still) be set
+        assertNotNull(app.getConfig(WebClusterDatabaseExampleApp.WAR_PATH));
+        assertEquals(app.getConfig(WebClusterDatabaseExampleApp.USE_HTTPS), Boolean.FALSE);
+        assertNotNull(app.getConfig(WebClusterDatabaseExampleApp.DB_SETUP_SQL_URL));
+
+        // expect entities to be there
+        MySqlNode mysql = (MySqlNode) Iterables.find(app.getChildren(), Predicates.instanceOf(MySqlNode.class));
+        ControlledDynamicWebAppCluster web = (ControlledDynamicWebAppCluster) Iterables.find(app.getChildren(), Predicates.instanceOf(ControlledDynamicWebAppCluster.class));
+        final NginxController nginx = (NginxController) Iterables.find(web.getChildren(), Predicates.instanceOf(NginxController.class));
+        DynamicWebAppCluster webCluster = (DynamicWebAppCluster) Iterables.find(web.getChildren(), Predicates.instanceOf(DynamicWebAppCluster.class));
+        Collection<Entity> appservers = web.getMembers();
+        assertEquals(appservers.size(), 2);
+        String clusterUrl = checkNotNull(app.getAttribute(WebClusterDatabaseExampleApp.ROOT_URL), "cluster url");
+        String dbUrl = checkNotNull(mysql.getAttribute(MySqlNode.DATASTORE_URL), "database url");
+        final String expectedJdbcUrl = String.format("jdbc:%s%s?user=%s\\&password=%s", dbUrl, WebClusterDatabaseExampleApp.DB_TABLE, 
+                WebClusterDatabaseExampleApp.DB_USERNAME, WebClusterDatabaseExampleApp.DB_PASSWORD);
+
+        // expect web-app to be reachable, and wired up to database
+        HttpTestUtils.assertHttpStatusCodeEventuallyEquals(clusterUrl, 200);
+        for (Entity appserver : appservers) {
+            String appserverUrl = checkNotNull(appserver.getAttribute(Tomcat8Server.ROOT_URL), "appserver url of "+appserver);
+
+            HttpTestUtils.assertHttpStatusCodeEventuallyEquals(appserverUrl, 200);
+            assertEquals(expectedJdbcUrl, appserver.getConfig(JavaEntityMethods.javaSysProp("brooklyn.example.db.url")), "of "+appserver);
+        }
+
+        WebAppMonitor monitor = newWebAppMonitor(clusterUrl, 200);
+
+        // expect auto-scaler policy to be there, and to be functional (e.g. can trigger resize)
+        AutoScalerPolicy autoScalerPolicy = (AutoScalerPolicy) Iterables.find(webCluster.getPolicies(), Predicates.instanceOf(AutoScalerPolicy.class));
+        
+        autoScalerPolicy.config().set(AutoScalerPolicy.MIN_POOL_SIZE, 3);
+        EntityTestUtils.assertGroupSizeEqualsEventually(web, 3);
+        final Collection<Entity> webMembersAfterGrow = web.getMembers();
+        
+        for (final Entity appserver : webMembersAfterGrow) {
+            Asserts.succeedsEventually(MutableMap.of("timeout", Duration.TWO_MINUTES), new Runnable() {
+                @Override public void run() {
+                    String appserverUrl = checkNotNull(appserver.getAttribute(Tomcat8Server.ROOT_URL), "appserver url of "+appserver);
+                    HttpTestUtils.assertHttpStatusCodeEquals(appserverUrl, 200);
+                    assertEquals(expectedJdbcUrl, appserver.getConfig(JavaEntityMethods.javaSysProp("brooklyn.example.db.url")), "of "+appserver);
+                    Asserts.assertEqualsIgnoringOrder(nginx.getAttribute(NginxController.SERVER_POOL_TARGETS).keySet(), webMembersAfterGrow);
+                }});
+        }
+
+        // expect enrichers to be there
+        Iterables.find(web.getEnrichers(), Predicates.instanceOf(HttpLatencyDetector.class));
+        Iterable<Enricher> propagatorEnrichers = Iterables.filter(web.getEnrichers(), Predicates.instanceOf(Propagator.class));
+        assertEquals(Iterables.size(propagatorEnrichers), 3, "propagatorEnrichers="+propagatorEnrichers);
+
+        // Check we see evidence of the enrichers having an effect.
+        // Relying on WebAppMonitor to stimulate activity.
+        EntityTestUtils.assertAttributeEqualsEventually(app, WebClusterDatabaseExampleApp.APPSERVERS_COUNT, 3);
+        EntityTestUtils.assertAttributeChangesEventually(web, DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW);
+        EntityTestUtils.assertAttributeChangesEventually(app, DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW);
+        EntityTestUtils.assertAttributeChangesEventually(web, HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_MOST_RECENT);
+        EntityTestUtils.assertAttributeChangesEventually(web, HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW);
+
+        // Restore the web-cluster to its original size of 2
+        autoScalerPolicy.config().set(AutoScalerPolicy.MIN_POOL_SIZE, 2);
+        EntityTestUtils.assertGroupSizeEqualsEventually(web, 2);
+        
+        final Entity removedAppserver = Iterables.getOnlyElement(Sets.difference(ImmutableSet.copyOf(webMembersAfterGrow), ImmutableSet.copyOf(web.getMembers())));
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertFalse(Entities.isManaged(removedAppserver));
+            }});
+        
+        monitor.assertNoFailures("hitting nginx url");
+        monitor.terminate();
+    }
+}


[11/12] incubator-brooklyn git commit: brooklyn-example-simple-messaging-pubsub: add org.apache package prefix

Posted by al...@apache.org.
brooklyn-example-simple-messaging-pubsub: add org.apache package prefix


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/f2792fa1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/f2792fa1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/f2792fa1

Branch: refs/heads/master
Commit: f2792fa18a7bfb58f42938cde6f6bbaed49dacec
Parents: 44d2dc1
Author: Ciprian Ciubotariu <ch...@gmx.net>
Authored: Wed Jul 15 11:11:14 2015 +0300
Committer: Ciprian Ciubotariu <ch...@gmx.net>
Committed: Wed Jul 15 18:47:01 2015 +0300

----------------------------------------------------------------------
 .../java/brooklyn/demo/KafkaClusterExample.java | 58 ---------------
 .../src/main/java/brooklyn/demo/Publish.java    | 71 ------------------
 .../demo/StandaloneQpidBrokerExample.java       | 73 -------------------
 .../src/main/java/brooklyn/demo/Subscribe.java  | 76 --------------------
 .../brooklyn/demo/KafkaClusterExample.java      | 58 +++++++++++++++
 .../java/org/apache/brooklyn/demo/Publish.java  | 71 ++++++++++++++++++
 .../demo/StandaloneQpidBrokerExample.java       | 73 +++++++++++++++++++
 .../org/apache/brooklyn/demo/Subscribe.java     | 76 ++++++++++++++++++++
 8 files changed, 278 insertions(+), 278 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f2792fa1/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/KafkaClusterExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/KafkaClusterExample.java b/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/KafkaClusterExample.java
deleted file mode 100644
index ec921c9..0000000
--- a/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/KafkaClusterExample.java
+++ /dev/null
@@ -1,58 +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.demo;
-
-import java.util.List;
-
-import brooklyn.entity.basic.ApplicationBuilder;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.messaging.kafka.KafkaCluster;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.collect.Lists;
-
-/** Kafka Cluster Application */
-public class KafkaClusterExample extends ApplicationBuilder {
-
-    public static final String DEFAULT_LOCATION = "localhost";
-
-    /** Configure the application. */
-    protected void doBuild() {
-        addChild(EntitySpec.create(KafkaCluster.class)
-                .configure("startTimeout", 300) // 5 minutes
-                .configure("initialSize", 2));
-        // TODO set application display name?
-    }
-
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                .application(new KafkaClusterExample().appDisplayName("Kafka cluster application"))
-                .webconsolePort(port)
-                .location(location)
-                .start();
-
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f2792fa1/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/Publish.java
----------------------------------------------------------------------
diff --git a/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/Publish.java b/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/Publish.java
deleted file mode 100644
index 22c0a60..0000000
--- a/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/Publish.java
+++ /dev/null
@@ -1,71 +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.demo;
-
-import javax.jms.Connection;
-import javax.jms.MessageProducer;
-import javax.jms.Queue;
-import javax.jms.Session;
-import javax.jms.TextMessage;
-
-import org.apache.qpid.client.AMQConnectionFactory;
-import org.apache.qpid.configuration.ClientProperties;
-
-import com.google.common.base.Preconditions;
-import com.google.common.base.Throwables;
-
-/** Publishes messages to a queue on a Qpid broker at a given URL. */
-public class Publish {
-    public static final String QUEUE = "'amq.direct'/'testQueue'; { node: { type: queue } }";
-        
-    public static void main(String...argv) throws Exception {
-        Preconditions.checkElementIndex(0, argv.length, "Must specify broker URL");
-        String url = argv[0];
-        
-        // Set Qpid client properties
-        System.setProperty(ClientProperties.AMQP_VERSION, "0-10");
-        System.setProperty(ClientProperties.DEST_SYNTAX, "ADDR");
-
-        // Connect to the broker
-        AMQConnectionFactory factory = new AMQConnectionFactory(url);
-        Connection connection = factory.createConnection();
-        connection.start();
-        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
-        
-        try {
-            // Create a producer for the queue
-            Queue destination = session.createQueue(QUEUE);
-            MessageProducer messageProducer = session.createProducer(destination);
-
-            // Send 100 messages
-            for (int n = 0; n < 100; n++) {
-                String body = String.format("test message %03d", n+1);
-                TextMessage message = session.createTextMessage(body);
-                messageProducer.send(message);
-                System.out.printf("Sent message %s\n", body);
-            }
-        } catch (Exception e) {
-            System.err.printf("Error while sending - %s\n", e.getMessage());
-            System.err.printf("Cause: %s\n", Throwables.getStackTraceAsString(e));
-        } finally {
-            session.close();
-            connection.close();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f2792fa1/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/StandaloneQpidBrokerExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/StandaloneQpidBrokerExample.java b/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/StandaloneQpidBrokerExample.java
deleted file mode 100644
index 89f7874..0000000
--- a/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/StandaloneQpidBrokerExample.java
+++ /dev/null
@@ -1,73 +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.demo;
-
-import java.util.List;
-
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.messaging.amqp.AmqpServer;
-import brooklyn.entity.messaging.qpid.QpidBroker;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-
-/** Qpid Broker Application */
-public class StandaloneQpidBrokerExample extends AbstractApplication {
-
-    public static final String CUSTOM_CONFIG_PATH = "classpath://custom-config.xml";
-    public static final String PASSWD_PATH = "classpath://passwd";
-    public static final String QPID_BDBSTORE_JAR_PATH = "classpath://qpid-bdbstore-0.20.jar";
-    public static final String BDBSTORE_JAR_PATH = "classpath://je-5.0.34.jar";
-
-    public static final String DEFAULT_LOCATION = "localhost";
-    
-    @Override
-    public void initApp() {
-        // Configure the Qpid broker entity
-        QpidBroker broker = addChild(EntitySpec.create(QpidBroker.class)
-                .configure("amqpPort", 5672)
-                .configure("amqpVersion", AmqpServer.AMQP_0_10)
-                .configure("runtimeFiles", ImmutableMap.builder()
-                        .put(CUSTOM_CONFIG_PATH, QpidBroker.CONFIG_XML)
-                        .put(PASSWD_PATH, QpidBroker.PASSWD)
-                        .put(QPID_BDBSTORE_JAR_PATH, "lib/opt/qpid-bdbstore-0.20.jar")
-                        .put(BDBSTORE_JAR_PATH, "lib/opt/je-5.0.34.jar")
-                        .build())
-                .configure("queue", "testQueue"));
-    }
-
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                .application(EntitySpec.create(StartableApplication.class, StandaloneQpidBrokerExample.class).displayName("Qpid app"))
-                .webconsolePort(port)
-                .location(location)
-                .start();
-         
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f2792fa1/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/Subscribe.java
----------------------------------------------------------------------
diff --git a/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/Subscribe.java b/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/Subscribe.java
deleted file mode 100644
index 7272887..0000000
--- a/examples/simple-messaging-pubsub/src/main/java/brooklyn/demo/Subscribe.java
+++ /dev/null
@@ -1,76 +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.demo;
-
-import javax.jms.Connection;
-import javax.jms.MessageConsumer;
-import javax.jms.Queue;
-import javax.jms.Session;
-import javax.jms.TextMessage;
-
-import org.apache.qpid.client.AMQConnectionFactory;
-import org.apache.qpid.configuration.ClientProperties;
-
-import com.google.common.base.Preconditions;
-import com.google.common.base.Throwables;
-
-/** Receives messages from a queue on a Qpid broker at a given URL. */
-public class Subscribe {
-    public static final String QUEUE = "'amq.direct'/'testQueue'; { node: { type: queue } }";
-    private static final long MESSAGE_TIMEOUT_MILLIS = 15000L;
-    private static final int MESSAGE_COUNT = 100;
-    
-    public static void main(String...argv) throws Exception {
-        Preconditions.checkElementIndex(0, argv.length, "Must specify broker URL");
-        String url = argv[0];
-
-        // Set Qpid client properties
-        System.setProperty(ClientProperties.AMQP_VERSION, "0-10");
-        System.setProperty(ClientProperties.DEST_SYNTAX, "ADDR");
-
-        // Connect to the broker
-        AMQConnectionFactory factory = new AMQConnectionFactory(url);
-        Connection connection = factory.createConnection();
-        connection.start();
-        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
-
-        System.out.printf("Waiting up to %s milliseconds to receive %s messages\n", MESSAGE_TIMEOUT_MILLIS, MESSAGE_COUNT);
-        try {
-            // Create a producer for the queue
-            Queue destination = session.createQueue(QUEUE);
-            MessageConsumer messageConsumer = session.createConsumer(destination);
-
-            // Try and receive 100 messages
-            for (int n = 0; n < MESSAGE_COUNT; n++) {
-                TextMessage msg = (TextMessage) messageConsumer.receive(MESSAGE_TIMEOUT_MILLIS);
-                if (msg == null) {
-                    System.out.printf("No message received in %s milliseconds, exiting", MESSAGE_TIMEOUT_MILLIS);
-                    break;
-                }
-                System.out.printf("Got message %d: '%s'\n", n+1, msg.getText());
-            }
-        } catch (Exception e) {
-            System.err.printf("Error while receiving - %s\n", e.getMessage());
-            System.err.printf("Cause: %s\n", Throwables.getStackTraceAsString(e));
-        } finally {
-            session.close();
-            connection.close();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f2792fa1/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/KafkaClusterExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/KafkaClusterExample.java b/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/KafkaClusterExample.java
new file mode 100644
index 0000000..f0d361e
--- /dev/null
+++ b/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/KafkaClusterExample.java
@@ -0,0 +1,58 @@
+/*
+ * 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.demo;
+
+import java.util.List;
+
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.messaging.kafka.KafkaCluster;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.collect.Lists;
+
+/** Kafka Cluster Application */
+public class KafkaClusterExample extends ApplicationBuilder {
+
+    public static final String DEFAULT_LOCATION = "localhost";
+
+    /** Configure the application. */
+    protected void doBuild() {
+        addChild(EntitySpec.create(KafkaCluster.class)
+                .configure("startTimeout", 300) // 5 minutes
+                .configure("initialSize", 2));
+        // TODO set application display name?
+    }
+
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .application(new KafkaClusterExample().appDisplayName("Kafka cluster application"))
+                .webconsolePort(port)
+                .location(location)
+                .start();
+
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f2792fa1/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/Publish.java
----------------------------------------------------------------------
diff --git a/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/Publish.java b/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/Publish.java
new file mode 100644
index 0000000..8a68061
--- /dev/null
+++ b/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/Publish.java
@@ -0,0 +1,71 @@
+/*
+ * 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.demo;
+
+import javax.jms.Connection;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.configuration.ClientProperties;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+
+/** Publishes messages to a queue on a Qpid broker at a given URL. */
+public class Publish {
+    public static final String QUEUE = "'amq.direct'/'testQueue'; { node: { type: queue } }";
+        
+    public static void main(String...argv) throws Exception {
+        Preconditions.checkElementIndex(0, argv.length, "Must specify broker URL");
+        String url = argv[0];
+        
+        // Set Qpid client properties
+        System.setProperty(ClientProperties.AMQP_VERSION, "0-10");
+        System.setProperty(ClientProperties.DEST_SYNTAX, "ADDR");
+
+        // Connect to the broker
+        AMQConnectionFactory factory = new AMQConnectionFactory(url);
+        Connection connection = factory.createConnection();
+        connection.start();
+        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        
+        try {
+            // Create a producer for the queue
+            Queue destination = session.createQueue(QUEUE);
+            MessageProducer messageProducer = session.createProducer(destination);
+
+            // Send 100 messages
+            for (int n = 0; n < 100; n++) {
+                String body = String.format("test message %03d", n+1);
+                TextMessage message = session.createTextMessage(body);
+                messageProducer.send(message);
+                System.out.printf("Sent message %s\n", body);
+            }
+        } catch (Exception e) {
+            System.err.printf("Error while sending - %s\n", e.getMessage());
+            System.err.printf("Cause: %s\n", Throwables.getStackTraceAsString(e));
+        } finally {
+            session.close();
+            connection.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f2792fa1/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/StandaloneQpidBrokerExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/StandaloneQpidBrokerExample.java b/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/StandaloneQpidBrokerExample.java
new file mode 100644
index 0000000..9df7005
--- /dev/null
+++ b/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/StandaloneQpidBrokerExample.java
@@ -0,0 +1,73 @@
+/*
+ * 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.demo;
+
+import java.util.List;
+
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.messaging.amqp.AmqpServer;
+import brooklyn.entity.messaging.qpid.QpidBroker;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+
+/** Qpid Broker Application */
+public class StandaloneQpidBrokerExample extends AbstractApplication {
+
+    public static final String CUSTOM_CONFIG_PATH = "classpath://custom-config.xml";
+    public static final String PASSWD_PATH = "classpath://passwd";
+    public static final String QPID_BDBSTORE_JAR_PATH = "classpath://qpid-bdbstore-0.20.jar";
+    public static final String BDBSTORE_JAR_PATH = "classpath://je-5.0.34.jar";
+
+    public static final String DEFAULT_LOCATION = "localhost";
+    
+    @Override
+    public void initApp() {
+        // Configure the Qpid broker entity
+        QpidBroker broker = addChild(EntitySpec.create(QpidBroker.class)
+                .configure("amqpPort", 5672)
+                .configure("amqpVersion", AmqpServer.AMQP_0_10)
+                .configure("runtimeFiles", ImmutableMap.builder()
+                        .put(CUSTOM_CONFIG_PATH, QpidBroker.CONFIG_XML)
+                        .put(PASSWD_PATH, QpidBroker.PASSWD)
+                        .put(QPID_BDBSTORE_JAR_PATH, "lib/opt/qpid-bdbstore-0.20.jar")
+                        .put(BDBSTORE_JAR_PATH, "lib/opt/je-5.0.34.jar")
+                        .build())
+                .configure("queue", "testQueue"));
+    }
+
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .application(EntitySpec.create(StartableApplication.class, StandaloneQpidBrokerExample.class).displayName("Qpid app"))
+                .webconsolePort(port)
+                .location(location)
+                .start();
+         
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f2792fa1/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/Subscribe.java
----------------------------------------------------------------------
diff --git a/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/Subscribe.java b/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/Subscribe.java
new file mode 100644
index 0000000..b7df104
--- /dev/null
+++ b/examples/simple-messaging-pubsub/src/main/java/org/apache/brooklyn/demo/Subscribe.java
@@ -0,0 +1,76 @@
+/*
+ * 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.demo;
+
+import javax.jms.Connection;
+import javax.jms.MessageConsumer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.configuration.ClientProperties;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+
+/** Receives messages from a queue on a Qpid broker at a given URL. */
+public class Subscribe {
+    public static final String QUEUE = "'amq.direct'/'testQueue'; { node: { type: queue } }";
+    private static final long MESSAGE_TIMEOUT_MILLIS = 15000L;
+    private static final int MESSAGE_COUNT = 100;
+    
+    public static void main(String...argv) throws Exception {
+        Preconditions.checkElementIndex(0, argv.length, "Must specify broker URL");
+        String url = argv[0];
+
+        // Set Qpid client properties
+        System.setProperty(ClientProperties.AMQP_VERSION, "0-10");
+        System.setProperty(ClientProperties.DEST_SYNTAX, "ADDR");
+
+        // Connect to the broker
+        AMQConnectionFactory factory = new AMQConnectionFactory(url);
+        Connection connection = factory.createConnection();
+        connection.start();
+        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+        System.out.printf("Waiting up to %s milliseconds to receive %s messages\n", MESSAGE_TIMEOUT_MILLIS, MESSAGE_COUNT);
+        try {
+            // Create a producer for the queue
+            Queue destination = session.createQueue(QUEUE);
+            MessageConsumer messageConsumer = session.createConsumer(destination);
+
+            // Try and receive 100 messages
+            for (int n = 0; n < MESSAGE_COUNT; n++) {
+                TextMessage msg = (TextMessage) messageConsumer.receive(MESSAGE_TIMEOUT_MILLIS);
+                if (msg == null) {
+                    System.out.printf("No message received in %s milliseconds, exiting", MESSAGE_TIMEOUT_MILLIS);
+                    break;
+                }
+                System.out.printf("Got message %d: '%s'\n", n+1, msg.getText());
+            }
+        } catch (Exception e) {
+            System.err.printf("Error while receiving - %s\n", e.getMessage());
+            System.err.printf("Cause: %s\n", Throwables.getStackTraceAsString(e));
+        } finally {
+            session.close();
+            connection.close();
+        }
+    }
+}


[08/12] incubator-brooklyn git commit: brooklyn-example-simple-web-cluster: add org.apache package prefix

Posted by al...@apache.org.
brooklyn-example-simple-web-cluster: add org.apache package prefix


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/9b8bde02
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/9b8bde02
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/9b8bde02

Branch: refs/heads/master
Commit: 9b8bde02dc71d8b7183216d041b0dfc81924cbd3
Parents: 59cc05d
Author: Ciprian Ciubotariu <ch...@gmx.net>
Authored: Wed Jul 15 18:11:11 2015 +0300
Committer: Ciprian Ciubotariu <ch...@gmx.net>
Committed: Wed Jul 15 18:47:01 2015 +0300

----------------------------------------------------------------------
 .../src/main/assembly/scripts/start.sh          |   2 +-
 .../brooklyn/demo/NodeJsTodoApplication.java    |  59 ------
 .../brooklyn/demo/SingleWebServerExample.java   |  67 ------
 .../demo/WebClusterDatabaseExample.java         | 123 -----------
 .../demo/WebClusterDatabaseExampleApp.java      | 175 ----------------
 .../demo/WebClusterDatabaseExampleGroovy.groovy |  93 ---------
 .../java/brooklyn/demo/WebClusterExample.java   |  96 ---------
 .../brooklyn/demo/NodeJsTodoApplication.java    |  59 ++++++
 .../brooklyn/demo/SingleWebServerExample.java   |  67 ++++++
 .../demo/WebClusterDatabaseExample.java         | 123 +++++++++++
 .../demo/WebClusterDatabaseExampleApp.java      | 175 ++++++++++++++++
 .../demo/WebClusterDatabaseExampleGroovy.groovy |  93 +++++++++
 .../apache/brooklyn/demo/WebClusterExample.java |  96 +++++++++
 .../brooklyn/demo/glossy-3d-blue-web-icon.png   | Bin 46490 -> 0 bytes
 .../brooklyn/demo/nodejs-riak-todo.yaml         |  46 -----
 .../resources/brooklyn/demo/nodejs-todo.yaml    |  53 -----
 .../src/main/resources/logback-custom.xml       |   2 +-
 .../brooklyn/demo/glossy-3d-blue-web-icon.png   | Bin 0 -> 46490 bytes
 .../apache/brooklyn/demo/nodejs-riak-todo.yaml  |  46 +++++
 .../org/apache/brooklyn/demo/nodejs-todo.yaml   |  53 +++++
 ...lusterDatabaseExampleAppIntegrationTest.java | 205 ------------------
 ...lusterDatabaseExampleAppIntegrationTest.java | 206 +++++++++++++++++++
 22 files changed, 920 insertions(+), 919 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/assembly/scripts/start.sh
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/assembly/scripts/start.sh b/examples/simple-web-cluster/src/main/assembly/scripts/start.sh
index a4cf2d4..ec4f110 100755
--- a/examples/simple-web-cluster/src/main/assembly/scripts/start.sh
+++ b/examples/simple-web-cluster/src/main/assembly/scripts/start.sh
@@ -19,7 +19,7 @@
 #
 
 if [ -z "$BROOKLYN_APP_CLASS" ] ; then 
-    BROOKLYN_APP_CLASS=brooklyn.demo.WebClusterDatabaseExample
+    BROOKLYN_APP_CLASS=org.apache.brooklyn.demo.WebClusterDatabaseExample
 fi
 
 if [ -z "$JAVA" ] ; then 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/brooklyn/demo/NodeJsTodoApplication.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/brooklyn/demo/NodeJsTodoApplication.java b/examples/simple-web-cluster/src/main/java/brooklyn/demo/NodeJsTodoApplication.java
deleted file mode 100644
index 50d5f74..0000000
--- a/examples/simple-web-cluster/src/main/java/brooklyn/demo/NodeJsTodoApplication.java
+++ /dev/null
@@ -1,59 +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.demo;
-
-import static brooklyn.event.basic.DependentConfiguration.attributeWhenReady;
-import brooklyn.catalog.Catalog;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.Attributes;
-import brooklyn.entity.basic.SoftwareProcess;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.nosql.redis.RedisStore;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.trait.Startable;
-import brooklyn.entity.webapp.nodejs.NodeJsWebAppService;
-import brooklyn.event.basic.DependentConfiguration;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-/**
- * Node.JS Todo Application
- */
-@Catalog(name="NodeJS Todo",
-        description="Node.js is a cross-platform runtime environment for server-side and networking applications. Node.js applications are written in JavaScript",
-        iconUrl="classpath://nodejs-logo.png")
-public class NodeJsTodoApplication extends AbstractApplication implements StartableApplication {
-
-    @Override
-    public void initApp() {
-        RedisStore redis = addChild(EntitySpec.create(RedisStore.class));
-
-        addChild(EntitySpec.create(NodeJsWebAppService.class)
-                .configure(NodeJsWebAppService.APP_GIT_REPOSITORY_URL, "https://github.com/grkvlt/nodejs-todo/")
-                .configure(NodeJsWebAppService.APP_FILE, "server.js")
-                .configure(NodeJsWebAppService.APP_NAME, "nodejs-todo")
-                .configure(NodeJsWebAppService.NODE_PACKAGE_LIST, ImmutableList.of("express", "ejs", "jasmine-node", "underscore", "method-override", "cookie-parser", "express-session", "body-parser", "cookie-session", "redis", "redis-url", "connect"))
-                .configure(SoftwareProcess.SHELL_ENVIRONMENT, ImmutableMap.<String, Object>of(
-                        "REDISTOGO_URL", DependentConfiguration.formatString("redis://%s:%d/",
-                                attributeWhenReady(redis, Attributes.HOSTNAME), attributeWhenReady(redis, RedisStore.REDIS_PORT))))
-                .configure(SoftwareProcess.LAUNCH_LATCH, attributeWhenReady(redis, Startable.SERVICE_UP)));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/brooklyn/demo/SingleWebServerExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/brooklyn/demo/SingleWebServerExample.java b/examples/simple-web-cluster/src/main/java/brooklyn/demo/SingleWebServerExample.java
deleted file mode 100644
index d974e35..0000000
--- a/examples/simple-web-cluster/src/main/java/brooklyn/demo/SingleWebServerExample.java
+++ /dev/null
@@ -1,67 +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.demo;
-
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.Attributes;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.webapp.JavaWebAppService;
-import brooklyn.entity.webapp.jboss.JBoss7Server;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.location.basic.PortRanges;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.collect.Lists;
-
-/** This example starts one web app on 8080, waits for a keypress, then stops it. */
-public class SingleWebServerExample extends AbstractApplication {
-
-    public static final Logger LOG = LoggerFactory.getLogger(SingleWebServerExample.class);
-
-    private static final String WAR_PATH = "classpath://hello-world-webapp.war";
-
-    @Override
-    public void initApp() {
-        addChild(EntitySpec.create(JBoss7Server.class)
-                .configure(JavaWebAppService.ROOT_WAR, WAR_PATH)
-                .configure(Attributes.HTTP_PORT, PortRanges.fromString("8080+")));
-    }
-
-    // Shows how to use ApplicationBuilder without sub-classing, but for CLI usage one should sub-class
-    public static void main(String[] argv) throws Exception {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", "localhost");
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                .application(EntitySpec.create(StartableApplication.class, SingleWebServerExample.class).displayName("Brooklyn WebApp example"))
-                .webconsolePort(port)
-                .location(location)
-                .start();
-         
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExample.java b/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExample.java
deleted file mode 100644
index fd7c9ae..0000000
--- a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExample.java
+++ /dev/null
@@ -1,123 +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.demo;
-
-import static brooklyn.entity.java.JavaEntityMethods.javaSysProp;
-import static brooklyn.event.basic.DependentConfiguration.attributeWhenReady;
-import static brooklyn.event.basic.DependentConfiguration.formatString;
-
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.enricher.Enrichers;
-import brooklyn.enricher.HttpLatencyDetector;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.database.mysql.MySqlNode;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
-import brooklyn.entity.webapp.DynamicWebAppCluster;
-import brooklyn.entity.webapp.JavaWebAppService;
-import brooklyn.entity.webapp.WebAppService;
-import brooklyn.entity.webapp.WebAppServiceConstants;
-import brooklyn.event.AttributeSensor;
-import brooklyn.event.basic.Sensors;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.location.basic.PortRanges;
-import brooklyn.policy.autoscaling.AutoScalerPolicy;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-
-/**
- * Launches a 3-tier app with nginx, clustered jboss, and mysql.
- **/
-public class WebClusterDatabaseExample extends AbstractApplication {
-    
-    public static final Logger LOG = LoggerFactory.getLogger(WebClusterDatabaseExample.class);
-    
-    public static final String WAR_PATH = "classpath://hello-world-sql-webapp.war";
-    
-    public static final String DB_SETUP_SQL_URL = "classpath://visitors-creation-script.sql";
-    
-    public static final String DB_TABLE = "visitors";
-    public static final String DB_USERNAME = "brooklyn";
-    public static final String DB_PASSWORD = "br00k11n";
-    
-    public static final AttributeSensor<Integer> APPSERVERS_COUNT = Sensors.newIntegerSensor( 
-            "appservers.count", "Number of app servers deployed");
-
-    @Override
-    public void initApp() {
-        MySqlNode mysql = addChild(EntitySpec.create(MySqlNode.class)
-                .configure("creationScriptUrl", DB_SETUP_SQL_URL));
-        
-        ControlledDynamicWebAppCluster web = addChild(EntitySpec.create(ControlledDynamicWebAppCluster.class)
-                .configure(WebAppService.HTTP_PORT, PortRanges.fromString("8080+"))
-                .configure(JavaWebAppService.ROOT_WAR, WAR_PATH)
-                .configure(javaSysProp("brooklyn.example.db.url"), 
-                        formatString("jdbc:%s%s?user=%s\\&password=%s", 
-                                attributeWhenReady(mysql, MySqlNode.DATASTORE_URL), 
-                                DB_TABLE, DB_USERNAME, DB_PASSWORD)) );
-
-        web.addEnricher(HttpLatencyDetector.builder().
-                url(ControlledDynamicWebAppCluster.ROOT_URL).
-                rollup(10, TimeUnit.SECONDS).
-                build());
-
-        // simple scaling policy
-        web.getCluster().addPolicy(AutoScalerPolicy.builder().
-                metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW_PER_NODE).
-                metricRange(10, 100).
-                sizeRange(1, 5).
-                build());
-
-        // expose some KPI's
-        addEnricher(Enrichers.builder()
-                .propagating(WebAppServiceConstants.ROOT_URL,
-                        DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW,
-                        HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW)
-                .from(web)
-                .build());
-
-        addEnricher(Enrichers.builder()
-                .propagating(ImmutableMap.of(DynamicWebAppCluster.GROUP_SIZE, APPSERVERS_COUNT))
-                .from(web)
-                .build());
-    }
-
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", "localhost");
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                .application(EntitySpec.create(StartableApplication.class, WebClusterDatabaseExample.class).displayName("Brooklyn WebApp Cluster with Database example"))
-                .webconsolePort(port)
-                .location(location)
-                .start();
-         
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleApp.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleApp.java b/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleApp.java
deleted file mode 100644
index 44efa0c..0000000
--- a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleApp.java
+++ /dev/null
@@ -1,175 +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.demo;
-
-import static brooklyn.event.basic.DependentConfiguration.attributeWhenReady;
-import static brooklyn.event.basic.DependentConfiguration.formatString;
-
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.catalog.Catalog;
-import brooklyn.catalog.CatalogConfig;
-import brooklyn.config.ConfigKey;
-import brooklyn.enricher.Enrichers;
-import brooklyn.enricher.HttpLatencyDetector;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.database.mysql.MySqlNode;
-import brooklyn.entity.group.DynamicCluster;
-import brooklyn.entity.java.JavaEntityMethods;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
-import brooklyn.entity.webapp.DynamicWebAppCluster;
-import brooklyn.entity.webapp.JavaWebAppService;
-import brooklyn.entity.webapp.WebAppService;
-import brooklyn.entity.webapp.WebAppServiceConstants;
-import brooklyn.event.AttributeSensor;
-import brooklyn.event.basic.Sensors;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.location.basic.PortRanges;
-import brooklyn.policy.autoscaling.AutoScalerPolicy;
-import brooklyn.util.BrooklynMavenArtifacts;
-import brooklyn.util.CommandLineUtil;
-import brooklyn.util.ResourceUtils;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-
-/**
- * Launches a 3-tier app with nginx, clustered jboss, and mysql.
- * <p>
- * Includes some advanced features such as KPI / derived sensors,
- * and annotations for use in a catalog.
- * <p>
- * This variant also increases minimum size to 2.  
- * Note the policy min size must have the same value,
- * otherwise it fights with cluster set up trying to reduce the cluster size!
- **/
-@Catalog(name="Elastic Java Web + DB",
-    description="Deploys a WAR to a load-balanced elastic Java AppServer cluster, " +
-            "with an auto-scaling policy, " +
-            "wired to a database initialized with the provided SQL; " +
-            "defaults to a 'Hello World' chatroom app.",
-    iconUrl="classpath://brooklyn/demo/glossy-3d-blue-web-icon.png")
-public class WebClusterDatabaseExampleApp extends AbstractApplication implements StartableApplication {
-    
-    public static final Logger LOG = LoggerFactory.getLogger(WebClusterDatabaseExampleApp.class);
-    
-    public static final String DEFAULT_LOCATION = "localhost";
-
-    public static final String DEFAULT_WAR_PATH = ResourceUtils.create(WebClusterDatabaseExampleApp.class)
-            // take this war, from the classpath, or via maven if not on the classpath
-            .firstAvailableUrl(
-                    "classpath://hello-world-sql-webapp.war",
-                    BrooklynMavenArtifacts.localUrl("example", "brooklyn-example-hello-world-sql-webapp", "war"))
-            .or("classpath://hello-world-sql-webapp.war");
-    
-    @CatalogConfig(label="WAR (URL)", priority=2)
-    public static final ConfigKey<String> WAR_PATH = ConfigKeys.newConfigKey(
-        "app.war", "URL to the application archive which should be deployed", 
-        DEFAULT_WAR_PATH);    
-
-    // TODO to expose in catalog we need to let the keystore url be specified (not hard)
-    // and also confirm that this works for nginx (might be a bit fiddly);
-    // booleans in the gui are working (With checkbox)
-    @CatalogConfig(label="HTTPS")
-    public static final ConfigKey<Boolean> USE_HTTPS = ConfigKeys.newConfigKey(
-            "app.https", "Whether the application should use HTTPS only or just HTTP only (default)", false);
-    
-    public static final String DEFAULT_DB_SETUP_SQL_URL = "classpath://visitors-creation-script.sql";
-    
-    @CatalogConfig(label="DB Setup SQL (URL)", priority=1)
-    public static final ConfigKey<String> DB_SETUP_SQL_URL = ConfigKeys.newConfigKey(
-        "app.db_sql", "URL to the SQL script to set up the database", 
-        DEFAULT_DB_SETUP_SQL_URL);
-    
-    public static final String DB_TABLE = "visitors";
-    public static final String DB_USERNAME = "brooklyn";
-    public static final String DB_PASSWORD = "br00k11n";
-    
-    public static final AttributeSensor<Integer> APPSERVERS_COUNT = Sensors.newIntegerSensor( 
-            "appservers.count", "Number of app servers deployed");
-    public static final AttributeSensor<Double> REQUESTS_PER_SECOND_IN_WINDOW = 
-            WebAppServiceConstants.REQUESTS_PER_SECOND_IN_WINDOW;
-    public static final AttributeSensor<String> ROOT_URL = WebAppServiceConstants.ROOT_URL;
-
-    @Override
-    public void initApp() {
-        MySqlNode mysql = addChild(
-                EntitySpec.create(MySqlNode.class)
-                        .configure(MySqlNode.CREATION_SCRIPT_URL, Entities.getRequiredUrlConfig(this, DB_SETUP_SQL_URL)));
-
-        ControlledDynamicWebAppCluster web = addChild(
-                EntitySpec.create(ControlledDynamicWebAppCluster.class)
-                        .configure(WebAppService.HTTP_PORT, PortRanges.fromString("8080+"))
-                        // to specify a diferrent appserver:
-//                        .configure(ControlledDynamicWebAppCluster.MEMBER_SPEC, EntitySpec.create(TomcatServer.class))
-                        .configure(JavaWebAppService.ROOT_WAR, Entities.getRequiredUrlConfig(this, WAR_PATH))
-                        .configure(JavaEntityMethods.javaSysProp("brooklyn.example.db.url"), 
-                                formatString("jdbc:%s%s?user=%s\\&password=%s", 
-                                        attributeWhenReady(mysql, MySqlNode.DATASTORE_URL), DB_TABLE, DB_USERNAME, DB_PASSWORD))
-                        .configure(DynamicCluster.INITIAL_SIZE, 2)
-                        .configure(WebAppService.ENABLED_PROTOCOLS, ImmutableSet.of(getConfig(USE_HTTPS) ? "https" : "http")) );
-
-        web.addEnricher(HttpLatencyDetector.builder().
-                url(ROOT_URL).
-                rollup(10, TimeUnit.SECONDS).
-                build());
-        
-        web.getCluster().addPolicy(AutoScalerPolicy.builder().
-                metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW_PER_NODE).
-                metricRange(10, 100).
-                sizeRange(2, 5).
-                build());
-
-        addEnricher(Enrichers.builder()
-                .propagating(WebAppServiceConstants.ROOT_URL,
-                        DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW,
-                        HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW)
-                .from(web)
-                .build());
-
-        addEnricher(Enrichers.builder()
-                .propagating(ImmutableMap.of(DynamicWebAppCluster.GROUP_SIZE, APPSERVERS_COUNT))
-                .from(web)
-                .build());
-    }
-    
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                 .application(EntitySpec.create(StartableApplication.class, WebClusterDatabaseExampleApp.class)
-                         .displayName("Brooklyn WebApp Cluster with Database example"))
-                 .webconsolePort(port)
-                 .location(location)
-                 .start();
-             
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleGroovy.groovy
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleGroovy.groovy b/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleGroovy.groovy
deleted file mode 100644
index c3f6cc7..0000000
--- a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleGroovy.groovy
+++ /dev/null
@@ -1,93 +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.demo;
-
-import static brooklyn.entity.java.JavaEntityMethods.javaSysProp
-import static brooklyn.event.basic.DependentConfiguration.attributeWhenReady
-import static brooklyn.event.basic.DependentConfiguration.formatString
-
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
-import brooklyn.entity.basic.AbstractApplication
-import brooklyn.entity.basic.Entities
-import brooklyn.entity.database.mysql.MySqlNode
-import brooklyn.entity.proxying.EntitySpec
-import brooklyn.entity.webapp.ControlledDynamicWebAppCluster
-import brooklyn.entity.webapp.DynamicWebAppCluster
-import brooklyn.launcher.BrooklynLauncher
-import brooklyn.policy.autoscaling.AutoScalerPolicy
-import brooklyn.util.CommandLineUtil
-
-import com.google.common.collect.Lists
-
-/**
- * Launches a 3-tier app with nginx, clustered jboss, and mysql.
- * <p>
- * This variant of {@link WebClusterDatabaseExample} demonstrates <i>Groovy</i> language conveniences.
- **/
-public class WebClusterDatabaseExampleGroovy extends AbstractApplication {
-    
-    public static final Logger LOG = LoggerFactory.getLogger(WebClusterDatabaseExampleGroovy.class);
-    
-    public static final String DEFAULT_LOCATION = "localhost";
-
-    public static final String WAR_PATH = "classpath://hello-world-sql-webapp.war";
-    
-    public static final String DB_SETUP_SQL_URL = "classpath://visitors-creation-script.sql";
-    
-    public static final String DB_TABLE = "visitors";
-    public static final String DB_USERNAME = "brooklyn";
-    public static final String DB_PASSWORD = "br00k11n";
-    
-    @Override
-    public void initApp() {
-        MySqlNode mysql = addChild(MySqlNode,
-                creationScriptUrl: DB_SETUP_SQL_URL);
-        
-        ControlledDynamicWebAppCluster web = addChild(ControlledDynamicWebAppCluster,
-                war: WAR_PATH,
-                httpPort: "8080+",
-                (javaSysProp("brooklyn.example.db.url")): 
-                    formatString("jdbc:%s%s?user=%s\\&password=%s", 
-                            attributeWhenReady(mysql, MySqlNode.DATASTORE_URL), 
-                            DB_TABLE, DB_USERNAME, DB_PASSWORD));
-    
-        web.getCluster().addPolicy(AutoScalerPolicy.builder().
-                metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_LAST_PER_NODE).
-                sizeRange(1, 5).
-                metricRange(10, 100).
-                build());
-    }
-
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                .application(EntitySpec.create(WebClusterDatabaseExampleGroovy.class).displayName("Brooklyn WebApp Cluster with Database example"))
-                .webconsolePort(port)
-                .location(location)
-                .start();
-         
-        Entities.dumpInfo(launcher.getApplications());
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterExample.java b/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterExample.java
deleted file mode 100644
index 6a5d064..0000000
--- a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterExample.java
+++ /dev/null
@@ -1,96 +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.demo;
-
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.BrooklynProperties;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.proxy.nginx.NginxController;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
-import brooklyn.entity.webapp.DynamicWebAppCluster;
-import brooklyn.entity.webapp.jboss.JBoss7Server;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.policy.autoscaling.AutoScalerPolicy;
-import brooklyn.util.CommandLineUtil;
-
-import com.google.common.collect.Lists;
-
-/**
- * Launches a clustered and load-balanced set of web servers.
- * Demonstrates syntax, so many of the options used here are the defaults.
- * (So the class could be much simpler, as in WebClusterExampleAlt.)
- * <p>
- * Requires: 
- * -Xmx512m -Xms128m -XX:MaxPermSize=256m
- * and brooklyn-all jar, and this jar or classes dir, on classpath. 
- **/
-public class WebClusterExample extends AbstractApplication {
-    public static final Logger LOG = LoggerFactory.getLogger(WebClusterExample.class);
-    
-    static BrooklynProperties config = BrooklynProperties.Factory.newDefault();
-
-    public static final String DEFAULT_LOCATION = "localhost";
-
-    public static final String WAR_PATH = "classpath://hello-world-webapp.war";
-
-    private NginxController nginxController;
-    private ControlledDynamicWebAppCluster web;
-    
-    @Override
-    public void initApp() {
-        nginxController = addChild(EntitySpec.create(NginxController.class)
-                //.configure("domain", "webclusterexample.brooklyn.local")
-                .configure("port", "8000+"));
-          
-        web = addChild(EntitySpec.create(ControlledDynamicWebAppCluster.class)
-                .displayName("WebApp cluster")
-                .configure(ControlledDynamicWebAppCluster.CONTROLLER, nginxController)
-                .configure(ControlledDynamicWebAppCluster.INITIAL_SIZE, 1)
-                .configure(ControlledDynamicWebAppCluster.MEMBER_SPEC, EntitySpec.create(JBoss7Server.class)
-                        .configure("httpPort", "8080+")
-                        .configure("war", WAR_PATH)));
-        
-        web.getCluster().addPolicy(AutoScalerPolicy.builder()
-                .metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW_PER_NODE)
-                .sizeRange(1, 5)
-                .metricRange(10, 100)
-                .build());
-    }
-    
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
-
-        // TODO Want to parse, to handle multiple locations
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                .application(EntitySpec.create(WebClusterExample.class).displayName("Brooklyn WebApp Cluster example"))
-                .webconsolePort(port)
-                .location(location)
-                .start();
-         
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/NodeJsTodoApplication.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/NodeJsTodoApplication.java b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/NodeJsTodoApplication.java
new file mode 100644
index 0000000..bfae7ce
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/NodeJsTodoApplication.java
@@ -0,0 +1,59 @@
+/*
+ * 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.demo;
+
+import static brooklyn.event.basic.DependentConfiguration.attributeWhenReady;
+import brooklyn.catalog.Catalog;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.SoftwareProcess;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.nosql.redis.RedisStore;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.trait.Startable;
+import brooklyn.entity.webapp.nodejs.NodeJsWebAppService;
+import brooklyn.event.basic.DependentConfiguration;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Node.JS Todo Application
+ */
+@Catalog(name="NodeJS Todo",
+        description="Node.js is a cross-platform runtime environment for server-side and networking applications. Node.js applications are written in JavaScript",
+        iconUrl="classpath://nodejs-logo.png")
+public class NodeJsTodoApplication extends AbstractApplication implements StartableApplication {
+
+    @Override
+    public void initApp() {
+        RedisStore redis = addChild(EntitySpec.create(RedisStore.class));
+
+        addChild(EntitySpec.create(NodeJsWebAppService.class)
+                .configure(NodeJsWebAppService.APP_GIT_REPOSITORY_URL, "https://github.com/grkvlt/nodejs-todo/")
+                .configure(NodeJsWebAppService.APP_FILE, "server.js")
+                .configure(NodeJsWebAppService.APP_NAME, "nodejs-todo")
+                .configure(NodeJsWebAppService.NODE_PACKAGE_LIST, ImmutableList.of("express", "ejs", "jasmine-node", "underscore", "method-override", "cookie-parser", "express-session", "body-parser", "cookie-session", "redis", "redis-url", "connect"))
+                .configure(SoftwareProcess.SHELL_ENVIRONMENT, ImmutableMap.<String, Object>of(
+                        "REDISTOGO_URL", DependentConfiguration.formatString("redis://%s:%d/",
+                                attributeWhenReady(redis, Attributes.HOSTNAME), attributeWhenReady(redis, RedisStore.REDIS_PORT))))
+                .configure(SoftwareProcess.LAUNCH_LATCH, attributeWhenReady(redis, Startable.SERVICE_UP)));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/SingleWebServerExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/SingleWebServerExample.java b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/SingleWebServerExample.java
new file mode 100644
index 0000000..4561e2c
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/SingleWebServerExample.java
@@ -0,0 +1,67 @@
+/*
+ * 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.demo;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.webapp.JavaWebAppService;
+import brooklyn.entity.webapp.jboss.JBoss7Server;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.location.basic.PortRanges;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.collect.Lists;
+
+/** This example starts one web app on 8080, waits for a keypress, then stops it. */
+public class SingleWebServerExample extends AbstractApplication {
+
+    public static final Logger LOG = LoggerFactory.getLogger(SingleWebServerExample.class);
+
+    private static final String WAR_PATH = "classpath://hello-world-webapp.war";
+
+    @Override
+    public void initApp() {
+        addChild(EntitySpec.create(JBoss7Server.class)
+                .configure(JavaWebAppService.ROOT_WAR, WAR_PATH)
+                .configure(Attributes.HTTP_PORT, PortRanges.fromString("8080+")));
+    }
+
+    // Shows how to use ApplicationBuilder without sub-classing, but for CLI usage one should sub-class
+    public static void main(String[] argv) throws Exception {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", "localhost");
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .application(EntitySpec.create(StartableApplication.class, SingleWebServerExample.class).displayName("Brooklyn WebApp example"))
+                .webconsolePort(port)
+                .location(location)
+                .start();
+         
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExample.java b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExample.java
new file mode 100644
index 0000000..4f10eb1
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExample.java
@@ -0,0 +1,123 @@
+/*
+ * 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.demo;
+
+import static brooklyn.entity.java.JavaEntityMethods.javaSysProp;
+import static brooklyn.event.basic.DependentConfiguration.attributeWhenReady;
+import static brooklyn.event.basic.DependentConfiguration.formatString;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.enricher.Enrichers;
+import brooklyn.enricher.HttpLatencyDetector;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.database.mysql.MySqlNode;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
+import brooklyn.entity.webapp.DynamicWebAppCluster;
+import brooklyn.entity.webapp.JavaWebAppService;
+import brooklyn.entity.webapp.WebAppService;
+import brooklyn.entity.webapp.WebAppServiceConstants;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.basic.Sensors;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.location.basic.PortRanges;
+import brooklyn.policy.autoscaling.AutoScalerPolicy;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+
+/**
+ * Launches a 3-tier app with nginx, clustered jboss, and mysql.
+ **/
+public class WebClusterDatabaseExample extends AbstractApplication {
+    
+    public static final Logger LOG = LoggerFactory.getLogger(WebClusterDatabaseExample.class);
+    
+    public static final String WAR_PATH = "classpath://hello-world-sql-webapp.war";
+    
+    public static final String DB_SETUP_SQL_URL = "classpath://visitors-creation-script.sql";
+    
+    public static final String DB_TABLE = "visitors";
+    public static final String DB_USERNAME = "brooklyn";
+    public static final String DB_PASSWORD = "br00k11n";
+    
+    public static final AttributeSensor<Integer> APPSERVERS_COUNT = Sensors.newIntegerSensor( 
+            "appservers.count", "Number of app servers deployed");
+
+    @Override
+    public void initApp() {
+        MySqlNode mysql = addChild(EntitySpec.create(MySqlNode.class)
+                .configure("creationScriptUrl", DB_SETUP_SQL_URL));
+        
+        ControlledDynamicWebAppCluster web = addChild(EntitySpec.create(ControlledDynamicWebAppCluster.class)
+                .configure(WebAppService.HTTP_PORT, PortRanges.fromString("8080+"))
+                .configure(JavaWebAppService.ROOT_WAR, WAR_PATH)
+                .configure(javaSysProp("brooklyn.example.db.url"), 
+                        formatString("jdbc:%s%s?user=%s\\&password=%s", 
+                                attributeWhenReady(mysql, MySqlNode.DATASTORE_URL), 
+                                DB_TABLE, DB_USERNAME, DB_PASSWORD)) );
+
+        web.addEnricher(HttpLatencyDetector.builder().
+                url(ControlledDynamicWebAppCluster.ROOT_URL).
+                rollup(10, TimeUnit.SECONDS).
+                build());
+
+        // simple scaling policy
+        web.getCluster().addPolicy(AutoScalerPolicy.builder().
+                metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW_PER_NODE).
+                metricRange(10, 100).
+                sizeRange(1, 5).
+                build());
+
+        // expose some KPI's
+        addEnricher(Enrichers.builder()
+                .propagating(WebAppServiceConstants.ROOT_URL,
+                        DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW,
+                        HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW)
+                .from(web)
+                .build());
+
+        addEnricher(Enrichers.builder()
+                .propagating(ImmutableMap.of(DynamicWebAppCluster.GROUP_SIZE, APPSERVERS_COUNT))
+                .from(web)
+                .build());
+    }
+
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", "localhost");
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .application(EntitySpec.create(StartableApplication.class, WebClusterDatabaseExample.class).displayName("Brooklyn WebApp Cluster with Database example"))
+                .webconsolePort(port)
+                .location(location)
+                .start();
+         
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleApp.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleApp.java b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleApp.java
new file mode 100644
index 0000000..3a5d54d
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleApp.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.demo;
+
+import static brooklyn.event.basic.DependentConfiguration.attributeWhenReady;
+import static brooklyn.event.basic.DependentConfiguration.formatString;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.catalog.Catalog;
+import brooklyn.catalog.CatalogConfig;
+import brooklyn.config.ConfigKey;
+import brooklyn.enricher.Enrichers;
+import brooklyn.enricher.HttpLatencyDetector;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.database.mysql.MySqlNode;
+import brooklyn.entity.group.DynamicCluster;
+import brooklyn.entity.java.JavaEntityMethods;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
+import brooklyn.entity.webapp.DynamicWebAppCluster;
+import brooklyn.entity.webapp.JavaWebAppService;
+import brooklyn.entity.webapp.WebAppService;
+import brooklyn.entity.webapp.WebAppServiceConstants;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.basic.Sensors;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.location.basic.PortRanges;
+import brooklyn.policy.autoscaling.AutoScalerPolicy;
+import brooklyn.util.BrooklynMavenArtifacts;
+import brooklyn.util.CommandLineUtil;
+import brooklyn.util.ResourceUtils;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+/**
+ * Launches a 3-tier app with nginx, clustered jboss, and mysql.
+ * <p>
+ * Includes some advanced features such as KPI / derived sensors,
+ * and annotations for use in a catalog.
+ * <p>
+ * This variant also increases minimum size to 2.  
+ * Note the policy min size must have the same value,
+ * otherwise it fights with cluster set up trying to reduce the cluster size!
+ **/
+@Catalog(name="Elastic Java Web + DB",
+    description="Deploys a WAR to a load-balanced elastic Java AppServer cluster, " +
+            "with an auto-scaling policy, " +
+            "wired to a database initialized with the provided SQL; " +
+            "defaults to a 'Hello World' chatroom app.",
+    iconUrl="classpath://brooklyn/demo/glossy-3d-blue-web-icon.png")
+public class WebClusterDatabaseExampleApp extends AbstractApplication implements StartableApplication {
+    
+    public static final Logger LOG = LoggerFactory.getLogger(WebClusterDatabaseExampleApp.class);
+    
+    public static final String DEFAULT_LOCATION = "localhost";
+
+    public static final String DEFAULT_WAR_PATH = ResourceUtils.create(WebClusterDatabaseExampleApp.class)
+            // take this war, from the classpath, or via maven if not on the classpath
+            .firstAvailableUrl(
+                    "classpath://hello-world-sql-webapp.war",
+                    BrooklynMavenArtifacts.localUrl("example", "brooklyn-example-hello-world-sql-webapp", "war"))
+            .or("classpath://hello-world-sql-webapp.war");
+    
+    @CatalogConfig(label="WAR (URL)", priority=2)
+    public static final ConfigKey<String> WAR_PATH = ConfigKeys.newConfigKey(
+        "app.war", "URL to the application archive which should be deployed", 
+        DEFAULT_WAR_PATH);    
+
+    // TODO to expose in catalog we need to let the keystore url be specified (not hard)
+    // and also confirm that this works for nginx (might be a bit fiddly);
+    // booleans in the gui are working (With checkbox)
+    @CatalogConfig(label="HTTPS")
+    public static final ConfigKey<Boolean> USE_HTTPS = ConfigKeys.newConfigKey(
+            "app.https", "Whether the application should use HTTPS only or just HTTP only (default)", false);
+    
+    public static final String DEFAULT_DB_SETUP_SQL_URL = "classpath://visitors-creation-script.sql";
+    
+    @CatalogConfig(label="DB Setup SQL (URL)", priority=1)
+    public static final ConfigKey<String> DB_SETUP_SQL_URL = ConfigKeys.newConfigKey(
+        "app.db_sql", "URL to the SQL script to set up the database", 
+        DEFAULT_DB_SETUP_SQL_URL);
+    
+    public static final String DB_TABLE = "visitors";
+    public static final String DB_USERNAME = "brooklyn";
+    public static final String DB_PASSWORD = "br00k11n";
+    
+    public static final AttributeSensor<Integer> APPSERVERS_COUNT = Sensors.newIntegerSensor( 
+            "appservers.count", "Number of app servers deployed");
+    public static final AttributeSensor<Double> REQUESTS_PER_SECOND_IN_WINDOW = 
+            WebAppServiceConstants.REQUESTS_PER_SECOND_IN_WINDOW;
+    public static final AttributeSensor<String> ROOT_URL = WebAppServiceConstants.ROOT_URL;
+
+    @Override
+    public void initApp() {
+        MySqlNode mysql = addChild(
+                EntitySpec.create(MySqlNode.class)
+                        .configure(MySqlNode.CREATION_SCRIPT_URL, Entities.getRequiredUrlConfig(this, DB_SETUP_SQL_URL)));
+
+        ControlledDynamicWebAppCluster web = addChild(
+                EntitySpec.create(ControlledDynamicWebAppCluster.class)
+                        .configure(WebAppService.HTTP_PORT, PortRanges.fromString("8080+"))
+                        // to specify a diferrent appserver:
+//                        .configure(ControlledDynamicWebAppCluster.MEMBER_SPEC, EntitySpec.create(TomcatServer.class))
+                        .configure(JavaWebAppService.ROOT_WAR, Entities.getRequiredUrlConfig(this, WAR_PATH))
+                        .configure(JavaEntityMethods.javaSysProp("brooklyn.example.db.url"), 
+                                formatString("jdbc:%s%s?user=%s\\&password=%s", 
+                                        attributeWhenReady(mysql, MySqlNode.DATASTORE_URL), DB_TABLE, DB_USERNAME, DB_PASSWORD))
+                        .configure(DynamicCluster.INITIAL_SIZE, 2)
+                        .configure(WebAppService.ENABLED_PROTOCOLS, ImmutableSet.of(getConfig(USE_HTTPS) ? "https" : "http")) );
+
+        web.addEnricher(HttpLatencyDetector.builder().
+                url(ROOT_URL).
+                rollup(10, TimeUnit.SECONDS).
+                build());
+        
+        web.getCluster().addPolicy(AutoScalerPolicy.builder().
+                metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW_PER_NODE).
+                metricRange(10, 100).
+                sizeRange(2, 5).
+                build());
+
+        addEnricher(Enrichers.builder()
+                .propagating(WebAppServiceConstants.ROOT_URL,
+                        DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW,
+                        HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW)
+                .from(web)
+                .build());
+
+        addEnricher(Enrichers.builder()
+                .propagating(ImmutableMap.of(DynamicWebAppCluster.GROUP_SIZE, APPSERVERS_COUNT))
+                .from(web)
+                .build());
+    }
+    
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                 .application(EntitySpec.create(StartableApplication.class, WebClusterDatabaseExampleApp.class)
+                         .displayName("Brooklyn WebApp Cluster with Database example"))
+                 .webconsolePort(port)
+                 .location(location)
+                 .start();
+             
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleGroovy.groovy
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleGroovy.groovy b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleGroovy.groovy
new file mode 100644
index 0000000..c3f6cc7
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleGroovy.groovy
@@ -0,0 +1,93 @@
+/*
+ * 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.demo;
+
+import static brooklyn.entity.java.JavaEntityMethods.javaSysProp
+import static brooklyn.event.basic.DependentConfiguration.attributeWhenReady
+import static brooklyn.event.basic.DependentConfiguration.formatString
+
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+import brooklyn.entity.basic.AbstractApplication
+import brooklyn.entity.basic.Entities
+import brooklyn.entity.database.mysql.MySqlNode
+import brooklyn.entity.proxying.EntitySpec
+import brooklyn.entity.webapp.ControlledDynamicWebAppCluster
+import brooklyn.entity.webapp.DynamicWebAppCluster
+import brooklyn.launcher.BrooklynLauncher
+import brooklyn.policy.autoscaling.AutoScalerPolicy
+import brooklyn.util.CommandLineUtil
+
+import com.google.common.collect.Lists
+
+/**
+ * Launches a 3-tier app with nginx, clustered jboss, and mysql.
+ * <p>
+ * This variant of {@link WebClusterDatabaseExample} demonstrates <i>Groovy</i> language conveniences.
+ **/
+public class WebClusterDatabaseExampleGroovy extends AbstractApplication {
+    
+    public static final Logger LOG = LoggerFactory.getLogger(WebClusterDatabaseExampleGroovy.class);
+    
+    public static final String DEFAULT_LOCATION = "localhost";
+
+    public static final String WAR_PATH = "classpath://hello-world-sql-webapp.war";
+    
+    public static final String DB_SETUP_SQL_URL = "classpath://visitors-creation-script.sql";
+    
+    public static final String DB_TABLE = "visitors";
+    public static final String DB_USERNAME = "brooklyn";
+    public static final String DB_PASSWORD = "br00k11n";
+    
+    @Override
+    public void initApp() {
+        MySqlNode mysql = addChild(MySqlNode,
+                creationScriptUrl: DB_SETUP_SQL_URL);
+        
+        ControlledDynamicWebAppCluster web = addChild(ControlledDynamicWebAppCluster,
+                war: WAR_PATH,
+                httpPort: "8080+",
+                (javaSysProp("brooklyn.example.db.url")): 
+                    formatString("jdbc:%s%s?user=%s\\&password=%s", 
+                            attributeWhenReady(mysql, MySqlNode.DATASTORE_URL), 
+                            DB_TABLE, DB_USERNAME, DB_PASSWORD));
+    
+        web.getCluster().addPolicy(AutoScalerPolicy.builder().
+                metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_LAST_PER_NODE).
+                sizeRange(1, 5).
+                metricRange(10, 100).
+                build());
+    }
+
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .application(EntitySpec.create(WebClusterDatabaseExampleGroovy.class).displayName("Brooklyn WebApp Cluster with Database example"))
+                .webconsolePort(port)
+                .location(location)
+                .start();
+         
+        Entities.dumpInfo(launcher.getApplications());
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterExample.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterExample.java b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterExample.java
new file mode 100644
index 0000000..51642aa
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterExample.java
@@ -0,0 +1,96 @@
+/*
+ * 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.demo;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.proxy.nginx.NginxController;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
+import brooklyn.entity.webapp.DynamicWebAppCluster;
+import brooklyn.entity.webapp.jboss.JBoss7Server;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.policy.autoscaling.AutoScalerPolicy;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Launches a clustered and load-balanced set of web servers.
+ * Demonstrates syntax, so many of the options used here are the defaults.
+ * (So the class could be much simpler, as in WebClusterExampleAlt.)
+ * <p>
+ * Requires: 
+ * -Xmx512m -Xms128m -XX:MaxPermSize=256m
+ * and brooklyn-all jar, and this jar or classes dir, on classpath. 
+ **/
+public class WebClusterExample extends AbstractApplication {
+    public static final Logger LOG = LoggerFactory.getLogger(WebClusterExample.class);
+    
+    static BrooklynProperties config = BrooklynProperties.Factory.newDefault();
+
+    public static final String DEFAULT_LOCATION = "localhost";
+
+    public static final String WAR_PATH = "classpath://hello-world-webapp.war";
+
+    private NginxController nginxController;
+    private ControlledDynamicWebAppCluster web;
+    
+    @Override
+    public void initApp() {
+        nginxController = addChild(EntitySpec.create(NginxController.class)
+                //.configure("domain", "webclusterexample.brooklyn.local")
+                .configure("port", "8000+"));
+          
+        web = addChild(EntitySpec.create(ControlledDynamicWebAppCluster.class)
+                .displayName("WebApp cluster")
+                .configure(ControlledDynamicWebAppCluster.CONTROLLER, nginxController)
+                .configure(ControlledDynamicWebAppCluster.INITIAL_SIZE, 1)
+                .configure(ControlledDynamicWebAppCluster.MEMBER_SPEC, EntitySpec.create(JBoss7Server.class)
+                        .configure("httpPort", "8080+")
+                        .configure("war", WAR_PATH)));
+        
+        web.getCluster().addPolicy(AutoScalerPolicy.builder()
+                .metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW_PER_NODE)
+                .sizeRange(1, 5)
+                .metricRange(10, 100)
+                .build());
+    }
+    
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String location = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION);
+
+        // TODO Want to parse, to handle multiple locations
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                .application(EntitySpec.create(WebClusterExample.class).displayName("Brooklyn WebApp Cluster example"))
+                .webconsolePort(port)
+                .location(location)
+                .start();
+         
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/resources/brooklyn/demo/glossy-3d-blue-web-icon.png
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/resources/brooklyn/demo/glossy-3d-blue-web-icon.png b/examples/simple-web-cluster/src/main/resources/brooklyn/demo/glossy-3d-blue-web-icon.png
deleted file mode 100644
index 542a1de..0000000
Binary files a/examples/simple-web-cluster/src/main/resources/brooklyn/demo/glossy-3d-blue-web-icon.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/resources/brooklyn/demo/nodejs-riak-todo.yaml
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/resources/brooklyn/demo/nodejs-riak-todo.yaml b/examples/simple-web-cluster/src/main/resources/brooklyn/demo/nodejs-riak-todo.yaml
deleted file mode 100644
index d963671..0000000
--- a/examples/simple-web-cluster/src/main/resources/brooklyn/demo/nodejs-riak-todo.yaml
+++ /dev/null
@@ -1,46 +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.
-
-name: "Node.JS Todo Application"
-origin: "https://github.com/amirrajan/nodejs-todo/"
-location:
-  jclouds:aws-ec2:us-west-1:
-    imageId: us-west-1/ami-c33cdd87
-services:
-- type: brooklyn.entity.nosql.riak.RiakCluster
-  initialSize: 2
-  id: mycluster
-  brooklyn.config:
-    provisioning.properties:
-      osFamily: centos
-      minCores: 4
-      minRam: 2048
-- type: brooklyn.entity.webapp.nodejs.NodeJsWebAppService
-  id: nodejs-riak1
-  name: "Node.JS"
-  brooklyn.config:
-    gitRepoUrl:
-      "https://github.com/bostko/nodejs-todo.git"
-    appFileName: server.js
-    appName: nodejs-todo
-    nodePackages:
-    - basho-riak-client
-    env:
-      NODE_ENV: production
-      RIAK_NODES: >
-        $brooklyn:component("mycluster").attributeWhenReady("riak.cluster.nodeListPbPort")
-    launch.latch: $brooklyn:component("mycluster").attributeWhenReady("service.isUp")
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/resources/brooklyn/demo/nodejs-todo.yaml
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/resources/brooklyn/demo/nodejs-todo.yaml b/examples/simple-web-cluster/src/main/resources/brooklyn/demo/nodejs-todo.yaml
deleted file mode 100644
index ea17556..0000000
--- a/examples/simple-web-cluster/src/main/resources/brooklyn/demo/nodejs-todo.yaml
+++ /dev/null
@@ -1,53 +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.
-
-id: nodejs-todo-application
-name: "Node.JS Todo Application"
-origin: "https://github.com/amirrajan/nodejs-todo/"
-locations:
-- jclouds:softlayer:ams01
-services:
-- type: brooklyn.entity.nosql.redis.RedisStore
-  id: redis
-  name: "Redis"
-- type: brooklyn.entity.webapp.nodejs.NodeJsWebAppService
-  id: nodejs
-  name: "Node.JS"
-  brooklyn.config:
-    gitRepoUrl:
-      "https://github.com/grkvlt/nodejs-todo/"
-    appFileName: server.js
-    appName: nodejs-todo
-    nodePackages:
-    - express
-    - ejs
-    - jasmine-node
-    - underscore
-    - method-override
-    - cookie-parser
-    - express-session
-    - body-parser
-    - cookie-session
-    - redis
-    - redis-url
-    - connect
-    env:
-      REDISTOGO_URL: >
-        $brooklyn:formatString("redis://%s:%d/",
-          component("redis").attributeWhenReady("host.subnet.hostname"),
-          component("redis").attributeWhenReady("redis.port"))
-    launch.latch: $brooklyn:component("redis").attributeWhenReady("service.isUp")
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/resources/logback-custom.xml
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/resources/logback-custom.xml b/examples/simple-web-cluster/src/main/resources/logback-custom.xml
index 0d403dd..02a8a82 100644
--- a/examples/simple-web-cluster/src/main/resources/logback-custom.xml
+++ b/examples/simple-web-cluster/src/main/resources/logback-custom.xml
@@ -27,7 +27,7 @@
     -->        
   
     <!-- include this category -->
-    <logger name="brooklyn.demo" level="DEBUG"/>
+    <logger name="org.apache.brooklyn.demo" level="DEBUG"/>
     
     <!-- log to simple-web-cluster.log -->
     <property name="logging.basename" scope="context" value="brooklyn-simple-web-cluster" />

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/glossy-3d-blue-web-icon.png
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/glossy-3d-blue-web-icon.png b/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/glossy-3d-blue-web-icon.png
new file mode 100644
index 0000000..542a1de
Binary files /dev/null and b/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/glossy-3d-blue-web-icon.png differ

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/nodejs-riak-todo.yaml
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/nodejs-riak-todo.yaml b/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/nodejs-riak-todo.yaml
new file mode 100644
index 0000000..d963671
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/nodejs-riak-todo.yaml
@@ -0,0 +1,46 @@
+# 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.
+
+name: "Node.JS Todo Application"
+origin: "https://github.com/amirrajan/nodejs-todo/"
+location:
+  jclouds:aws-ec2:us-west-1:
+    imageId: us-west-1/ami-c33cdd87
+services:
+- type: brooklyn.entity.nosql.riak.RiakCluster
+  initialSize: 2
+  id: mycluster
+  brooklyn.config:
+    provisioning.properties:
+      osFamily: centos
+      minCores: 4
+      minRam: 2048
+- type: brooklyn.entity.webapp.nodejs.NodeJsWebAppService
+  id: nodejs-riak1
+  name: "Node.JS"
+  brooklyn.config:
+    gitRepoUrl:
+      "https://github.com/bostko/nodejs-todo.git"
+    appFileName: server.js
+    appName: nodejs-todo
+    nodePackages:
+    - basho-riak-client
+    env:
+      NODE_ENV: production
+      RIAK_NODES: >
+        $brooklyn:component("mycluster").attributeWhenReady("riak.cluster.nodeListPbPort")
+    launch.latch: $brooklyn:component("mycluster").attributeWhenReady("service.isUp")
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/nodejs-todo.yaml
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/nodejs-todo.yaml b/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/nodejs-todo.yaml
new file mode 100644
index 0000000..ea17556
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/resources/org/apache/brooklyn/demo/nodejs-todo.yaml
@@ -0,0 +1,53 @@
+# 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.
+
+id: nodejs-todo-application
+name: "Node.JS Todo Application"
+origin: "https://github.com/amirrajan/nodejs-todo/"
+locations:
+- jclouds:softlayer:ams01
+services:
+- type: brooklyn.entity.nosql.redis.RedisStore
+  id: redis
+  name: "Redis"
+- type: brooklyn.entity.webapp.nodejs.NodeJsWebAppService
+  id: nodejs
+  name: "Node.JS"
+  brooklyn.config:
+    gitRepoUrl:
+      "https://github.com/grkvlt/nodejs-todo/"
+    appFileName: server.js
+    appName: nodejs-todo
+    nodePackages:
+    - express
+    - ejs
+    - jasmine-node
+    - underscore
+    - method-override
+    - cookie-parser
+    - express-session
+    - body-parser
+    - cookie-session
+    - redis
+    - redis-url
+    - connect
+    env:
+      REDISTOGO_URL: >
+        $brooklyn:formatString("redis://%s:%d/",
+          component("redis").attributeWhenReady("host.subnet.hostname"),
+          component("redis").attributeWhenReady("redis.port"))
+    launch.latch: $brooklyn:component("redis").attributeWhenReady("service.isUp")
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9b8bde02/examples/simple-web-cluster/src/test/java/brooklyn/demo/RebindWebClusterDatabaseExampleAppIntegrationTest.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/test/java/brooklyn/demo/RebindWebClusterDatabaseExampleAppIntegrationTest.java b/examples/simple-web-cluster/src/test/java/brooklyn/demo/RebindWebClusterDatabaseExampleAppIntegrationTest.java
deleted file mode 100644
index f2c735c..0000000
--- a/examples/simple-web-cluster/src/test/java/brooklyn/demo/RebindWebClusterDatabaseExampleAppIntegrationTest.java
+++ /dev/null
@@ -1,205 +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.demo;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-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.enricher.HttpLatencyDetector;
-import brooklyn.enricher.basic.Propagator;
-import brooklyn.entity.Entity;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.database.mysql.MySqlNode;
-import brooklyn.entity.group.DynamicCluster;
-import brooklyn.entity.java.JavaEntityMethods;
-import brooklyn.entity.proxy.nginx.NginxController;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.rebind.RebindOptions;
-import brooklyn.entity.rebind.RebindTestFixture;
-import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
-import brooklyn.entity.webapp.DynamicWebAppCluster;
-import brooklyn.entity.webapp.tomcat.Tomcat8Server;
-import brooklyn.location.Location;
-import brooklyn.policy.Enricher;
-import brooklyn.policy.autoscaling.AutoScalerPolicy;
-import brooklyn.test.Asserts;
-import brooklyn.test.EntityTestUtils;
-import brooklyn.test.HttpTestUtils;
-import brooklyn.test.WebAppMonitor;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-
-
-public class RebindWebClusterDatabaseExampleAppIntegrationTest extends RebindTestFixture<StartableApplication> {
-
-    private static final Logger LOG = LoggerFactory.getLogger(RebindWebClusterDatabaseExampleAppIntegrationTest.class);
-
-    private Location origLoc;
-    private List<WebAppMonitor> webAppMonitors = new CopyOnWriteArrayList<WebAppMonitor>();
-    private ExecutorService executor;
-
-    @BeforeMethod(alwaysRun=true)
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        origLoc = origManagementContext.getLocationRegistry().resolve("localhost");
-        executor = Executors.newCachedThreadPool();
-        webAppMonitors.clear();
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    @Override
-    public void tearDown() throws Exception {
-        for (WebAppMonitor monitor : webAppMonitors) {
-            monitor.terminate();
-        }
-        if (executor != null) executor.shutdownNow();
-        super.tearDown();
-    }
-    
-    @Override
-    protected StartableApplication createApp() {
-        StartableApplication result = origManagementContext.getEntityManager().createEntity(EntitySpec.create(StartableApplication.class)
-                .impl(WebClusterDatabaseExampleApp.class)
-                .configure(DynamicCluster.INITIAL_SIZE, 2));
-        Entities.startManagement(result, origManagementContext);
-        return result;
-    }
-    
-    private WebAppMonitor newWebAppMonitor(String url, int expectedResponseCode) {
-        WebAppMonitor monitor = new WebAppMonitor(url)
-//              .delayMillis(0) FIXME Re-enable to fast polling
-                .expectedResponseCode(expectedResponseCode)
-                .logFailures(LOG);
-        webAppMonitors.add(monitor);
-        executor.execute(monitor);
-        return monitor;
-    }
-    
-    @Test(groups="Integration")
-    public void testRestoresSimpleApp() throws Exception {
-        origApp.start(ImmutableList.of(origLoc));
-        
-        assertAppFunctional(origApp);
-        
-        String clusterUrl = checkNotNull(origApp.getAttribute(WebClusterDatabaseExampleApp.ROOT_URL), "cluster url");
-        WebAppMonitor monitor = newWebAppMonitor(clusterUrl, 200);
-        
-        newApp = rebind(RebindOptions.create().terminateOrigManagementContext(true));
-        assertAppFunctional(newApp);
-
-        // expect no failures during rebind
-        monitor.assertNoFailures("hitting nginx url");
-        monitor.terminate();
-    }
-    
-    private void assertAppFunctional(StartableApplication app) throws Exception {
-        // expect standard config to (still) be set
-        assertNotNull(app.getConfig(WebClusterDatabaseExampleApp.WAR_PATH));
-        assertEquals(app.getConfig(WebClusterDatabaseExampleApp.USE_HTTPS), Boolean.FALSE);
-        assertNotNull(app.getConfig(WebClusterDatabaseExampleApp.DB_SETUP_SQL_URL));
-
-        // expect entities to be there
-        MySqlNode mysql = (MySqlNode) Iterables.find(app.getChildren(), Predicates.instanceOf(MySqlNode.class));
-        ControlledDynamicWebAppCluster web = (ControlledDynamicWebAppCluster) Iterables.find(app.getChildren(), Predicates.instanceOf(ControlledDynamicWebAppCluster.class));
-        final NginxController nginx = (NginxController) Iterables.find(web.getChildren(), Predicates.instanceOf(NginxController.class));
-        DynamicWebAppCluster webCluster = (DynamicWebAppCluster) Iterables.find(web.getChildren(), Predicates.instanceOf(DynamicWebAppCluster.class));
-        Collection<Entity> appservers = web.getMembers();
-        assertEquals(appservers.size(), 2);
-        String clusterUrl = checkNotNull(app.getAttribute(WebClusterDatabaseExampleApp.ROOT_URL), "cluster url");
-        String dbUrl = checkNotNull(mysql.getAttribute(MySqlNode.DATASTORE_URL), "database url");
-        final String expectedJdbcUrl = String.format("jdbc:%s%s?user=%s\\&password=%s", dbUrl, WebClusterDatabaseExampleApp.DB_TABLE, 
-                WebClusterDatabaseExampleApp.DB_USERNAME, WebClusterDatabaseExampleApp.DB_PASSWORD);
-
-        // expect web-app to be reachable, and wired up to database
-        HttpTestUtils.assertHttpStatusCodeEventuallyEquals(clusterUrl, 200);
-        for (Entity appserver : appservers) {
-            String appserverUrl = checkNotNull(appserver.getAttribute(Tomcat8Server.ROOT_URL), "appserver url of "+appserver);
-
-            HttpTestUtils.assertHttpStatusCodeEventuallyEquals(appserverUrl, 200);
-            assertEquals(expectedJdbcUrl, appserver.getConfig(JavaEntityMethods.javaSysProp("brooklyn.example.db.url")), "of "+appserver);
-        }
-
-        WebAppMonitor monitor = newWebAppMonitor(clusterUrl, 200);
-
-        // expect auto-scaler policy to be there, and to be functional (e.g. can trigger resize)
-        AutoScalerPolicy autoScalerPolicy = (AutoScalerPolicy) Iterables.find(webCluster.getPolicies(), Predicates.instanceOf(AutoScalerPolicy.class));
-        
-        autoScalerPolicy.config().set(AutoScalerPolicy.MIN_POOL_SIZE, 3);
-        EntityTestUtils.assertGroupSizeEqualsEventually(web, 3);
-        final Collection<Entity> webMembersAfterGrow = web.getMembers();
-        
-        for (final Entity appserver : webMembersAfterGrow) {
-            Asserts.succeedsEventually(MutableMap.of("timeout", Duration.TWO_MINUTES), new Runnable() {
-                @Override public void run() {
-                    String appserverUrl = checkNotNull(appserver.getAttribute(Tomcat8Server.ROOT_URL), "appserver url of "+appserver);
-                    HttpTestUtils.assertHttpStatusCodeEquals(appserverUrl, 200);
-                    assertEquals(expectedJdbcUrl, appserver.getConfig(JavaEntityMethods.javaSysProp("brooklyn.example.db.url")), "of "+appserver);
-                    Asserts.assertEqualsIgnoringOrder(nginx.getAttribute(NginxController.SERVER_POOL_TARGETS).keySet(), webMembersAfterGrow);
-                }});
-        }
-
-        // expect enrichers to be there
-        Iterables.find(web.getEnrichers(), Predicates.instanceOf(HttpLatencyDetector.class));
-        Iterable<Enricher> propagatorEnrichers = Iterables.filter(web.getEnrichers(), Predicates.instanceOf(Propagator.class));
-        assertEquals(Iterables.size(propagatorEnrichers), 3, "propagatorEnrichers="+propagatorEnrichers);
-
-        // Check we see evidence of the enrichers having an effect.
-        // Relying on WebAppMonitor to stimulate activity.
-        EntityTestUtils.assertAttributeEqualsEventually(app, WebClusterDatabaseExampleApp.APPSERVERS_COUNT, 3);
-        EntityTestUtils.assertAttributeChangesEventually(web, DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW);
-        EntityTestUtils.assertAttributeChangesEventually(app, DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW);
-        EntityTestUtils.assertAttributeChangesEventually(web, HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_MOST_RECENT);
-        EntityTestUtils.assertAttributeChangesEventually(web, HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW);
-
-        // Restore the web-cluster to its original size of 2
-        autoScalerPolicy.config().set(AutoScalerPolicy.MIN_POOL_SIZE, 2);
-        EntityTestUtils.assertGroupSizeEqualsEventually(web, 2);
-        
-        final Entity removedAppserver = Iterables.getOnlyElement(Sets.difference(ImmutableSet.copyOf(webMembersAfterGrow), ImmutableSet.copyOf(web.getMembers())));
-        Asserts.succeedsEventually(new Runnable() {
-            @Override public void run() {
-                assertFalse(Entities.isManaged(removedAppserver));
-            }});
-        
-        monitor.assertNoFailures("hitting nginx url");
-        monitor.terminate();
-    }
-}



[09/12] incubator-brooklyn git commit: brooklyn-example-simple-nosql-cluster: add org.apache package prefix

Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/WideAreaCassandraCluster.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/WideAreaCassandraCluster.java b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/WideAreaCassandraCluster.java
new file mode 100644
index 0000000..a19f817
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/WideAreaCassandraCluster.java
@@ -0,0 +1,86 @@
+/*
+ * 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.demo;
+
+import java.util.Arrays;
+import java.util.List;
+
+import brooklyn.catalog.Catalog;
+import brooklyn.catalog.CatalogConfig;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.StartableApplication;
+import brooklyn.entity.nosql.cassandra.CassandraDatacenter;
+import brooklyn.entity.nosql.cassandra.CassandraFabric;
+import brooklyn.entity.nosql.cassandra.CassandraNode;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.policy.EnricherSpec;
+import brooklyn.policy.PolicySpec;
+import brooklyn.policy.ha.ServiceFailureDetector;
+import brooklyn.policy.ha.ServiceReplacer;
+import brooklyn.policy.ha.ServiceRestarter;
+import brooklyn.util.CommandLineUtil;
+
+import com.google.common.collect.Lists;
+
+@Catalog(name="Wide Area Cassandra Cluster Application", description="Deploy a Cassandra cluster across multiple geographies.")
+public class WideAreaCassandraCluster extends AbstractApplication {
+
+    public static final String DEFAULT_LOCATION_SPEC = "aws-ec2:us-east-1,rackspace-cloudservers-uk";
+    
+    @CatalogConfig(label="Initial Cluster Size (per location)", priority=2)
+    public static final ConfigKey<Integer> CASSANDRA_CLUSTER_SIZE = ConfigKeys.newConfigKey(
+        "cassandra.cluster.initialSize", "Initial size of the Cassandra clusterss", 2);      
+    
+    
+    
+    @Override
+    public void initApp() {
+        addChild(EntitySpec.create(CassandraFabric.class)
+                .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn")
+                .configure(CassandraDatacenter.INITIAL_SIZE, getConfig(CASSANDRA_CLUSTER_SIZE)) // per location
+                .configure(CassandraDatacenter.ENDPOINT_SNITCH_NAME, "brooklyn.entity.nosql.cassandra.customsnitch.MultiCloudSnitch")
+                .configure(CassandraNode.CUSTOM_SNITCH_JAR_URL, "classpath://brooklyn/entity/nosql/cassandra/cassandra-multicloud-snitch.jar")
+                .configure(CassandraFabric.MEMBER_SPEC, EntitySpec.create(CassandraDatacenter.class)
+                        .configure(CassandraDatacenter.MEMBER_SPEC, EntitySpec.create(CassandraNode.class)
+                                .enricher(EnricherSpec.create(ServiceFailureDetector.class))
+                                .policy(PolicySpec.create(ServiceRestarter.class)
+                                        .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, ServiceFailureDetector.ENTITY_FAILED)))
+                        .policy(PolicySpec.create(ServiceReplacer.class)
+                                .configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, ServiceRestarter.ENTITY_RESTART_FAILED))));
+    }
+    
+    public static void main(String[] argv) {
+        List<String> args = Lists.newArrayList(argv);
+        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
+        String locations = CommandLineUtil.getCommandLineOption(args, "--location", DEFAULT_LOCATION_SPEC);
+
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+                 .application(EntitySpec.create(StartableApplication.class, WideAreaCassandraCluster.class)
+                         .displayName("Cassandra"))
+                 .webconsolePort(port)
+                 .locations(Arrays.asList(locations))
+                 .start();
+             
+        Entities.dumpInfo(launcher.getApplications());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/ha-cassandra-cluster.yaml
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/ha-cassandra-cluster.yaml b/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/ha-cassandra-cluster.yaml
deleted file mode 100644
index 4a9ebc7..0000000
--- a/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/ha-cassandra-cluster.yaml
+++ /dev/null
@@ -1,45 +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.
-#
-# A YAML version of the HighAvailabilityCassandraCluster application
-#
-name: HA Cassandra cluster
-location: aws-ec2:us-east-1
-
-services:
-- type: brooklyn.entity.nosql.cassandra.CassandraDatacenter
-  name: Brooklyn
-  brooklyn.config:
-    initialSize: 6
-    enableAvailabilityZones: true
-    # See https://github.com/brooklyncentral/brooklyn/issues/973
-    availabilityZoneNames:
-    - us-east-1b
-    - us-east-1c
-    - us-east-1e
-    numAvailabilityZones: 3
-    snitchName: GossipingPropertyFileSnitch
-    memberSpec:
-      $brooklyn:entitySpec:
-        type: brooklyn.entity.nosql.cassandra.CassandraNode
-        brookyn.policies:
-        - type: brooklyn.policy.ha.ServiceRestarter
-        brooklyn.enrichers:
-        - type: brooklyn.policy.ha.ServiceFailureDetector
-  brooklyn.policies:
-  - type: brooklyn.policy.ha.ServiceReplacer

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/simple-cassandra-cluster.yaml
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/simple-cassandra-cluster.yaml b/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/simple-cassandra-cluster.yaml
deleted file mode 100644
index ff7b48c..0000000
--- a/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/simple-cassandra-cluster.yaml
+++ /dev/null
@@ -1,28 +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.
-#
-# A YAML version of the SimpleCassandraCluster application
-#
-name: Simple Cassandra cluster
-location: localhost
-
-services:
-- type: brooklyn.entity.nosql.cassandra.CassandraDatacenter
-  name: Brooklyn
-  brooklyn.config:
-    initialSize: 1

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/wide-area-cassandra-cluster.yaml
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/wide-area-cassandra-cluster.yaml b/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/wide-area-cassandra-cluster.yaml
deleted file mode 100644
index 3e44e39..0000000
--- a/examples/simple-nosql-cluster/src/main/resources/brooklyn/demo/wide-area-cassandra-cluster.yaml
+++ /dev/null
@@ -1,41 +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.
-#
-# A YAML version of the WideAreaCassandraCluster application
-#
-name: Wide Area Cassandra cluster
-locations:
-- aws-ec2:us-east-1
-- rackspace-cloudservers-uk
-
-services:
-- type: brooklyn.entity.nosql.cassandra.CassandraFabric
-  name: Brooklyn
-  brooklyn.config:
-    initialSize: 2
-    snitchName: brooklyn.entity.nosql.cassandra.customsnitch.MultiCloudSnitch
-    customSnitchJarUrl: classpath://brooklyn/entity/nosql/cassandra/cassandra-multicloud-snitch.jar
-    memberSpec:
-      $brooklyn:entitySpec:
-        type: brooklyn.entity.nosql.cassandra.CassandraNode
-        brookyn.policies:
-        - type: brooklyn.policy.ha.ServiceRestarter
-        brooklyn.enrichers:
-        - type: brooklyn.policy.ha.ServiceFailureDetector
-    brooklyn.policies:
-    - type: brooklyn.policy.ha.ServiceReplacer

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/ha-cassandra-cluster.yaml
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/ha-cassandra-cluster.yaml b/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/ha-cassandra-cluster.yaml
new file mode 100644
index 0000000..4a9ebc7
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/ha-cassandra-cluster.yaml
@@ -0,0 +1,45 @@
+#
+# 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.
+#
+# A YAML version of the HighAvailabilityCassandraCluster application
+#
+name: HA Cassandra cluster
+location: aws-ec2:us-east-1
+
+services:
+- type: brooklyn.entity.nosql.cassandra.CassandraDatacenter
+  name: Brooklyn
+  brooklyn.config:
+    initialSize: 6
+    enableAvailabilityZones: true
+    # See https://github.com/brooklyncentral/brooklyn/issues/973
+    availabilityZoneNames:
+    - us-east-1b
+    - us-east-1c
+    - us-east-1e
+    numAvailabilityZones: 3
+    snitchName: GossipingPropertyFileSnitch
+    memberSpec:
+      $brooklyn:entitySpec:
+        type: brooklyn.entity.nosql.cassandra.CassandraNode
+        brookyn.policies:
+        - type: brooklyn.policy.ha.ServiceRestarter
+        brooklyn.enrichers:
+        - type: brooklyn.policy.ha.ServiceFailureDetector
+  brooklyn.policies:
+  - type: brooklyn.policy.ha.ServiceReplacer

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/simple-cassandra-cluster.yaml
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/simple-cassandra-cluster.yaml b/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/simple-cassandra-cluster.yaml
new file mode 100644
index 0000000..ff7b48c
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/simple-cassandra-cluster.yaml
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+# A YAML version of the SimpleCassandraCluster application
+#
+name: Simple Cassandra cluster
+location: localhost
+
+services:
+- type: brooklyn.entity.nosql.cassandra.CassandraDatacenter
+  name: Brooklyn
+  brooklyn.config:
+    initialSize: 1

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44d2dc11/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/wide-area-cassandra-cluster.yaml
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/wide-area-cassandra-cluster.yaml b/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/wide-area-cassandra-cluster.yaml
new file mode 100644
index 0000000..3e44e39
--- /dev/null
+++ b/examples/simple-nosql-cluster/src/main/resources/org/apache/brooklyn/demo/wide-area-cassandra-cluster.yaml
@@ -0,0 +1,41 @@
+#
+# 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.
+#
+# A YAML version of the WideAreaCassandraCluster application
+#
+name: Wide Area Cassandra cluster
+locations:
+- aws-ec2:us-east-1
+- rackspace-cloudservers-uk
+
+services:
+- type: brooklyn.entity.nosql.cassandra.CassandraFabric
+  name: Brooklyn
+  brooklyn.config:
+    initialSize: 2
+    snitchName: brooklyn.entity.nosql.cassandra.customsnitch.MultiCloudSnitch
+    customSnitchJarUrl: classpath://brooklyn/entity/nosql/cassandra/cassandra-multicloud-snitch.jar
+    memberSpec:
+      $brooklyn:entitySpec:
+        type: brooklyn.entity.nosql.cassandra.CassandraNode
+        brookyn.policies:
+        - type: brooklyn.policy.ha.ServiceRestarter
+        brooklyn.enrichers:
+        - type: brooklyn.policy.ha.ServiceFailureDetector
+    brooklyn.policies:
+    - type: brooklyn.policy.ha.ServiceReplacer


[03/12] incubator-brooklyn git commit: brooklyn-qa: add org.apache package prefix

Posted by al...@apache.org.
brooklyn-qa: add org.apache package prefix


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/bd44bb8f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/bd44bb8f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/bd44bb8f

Branch: refs/heads/master
Commit: bd44bb8f18799d603308d19e98348c949ffa00a1
Parents: 0d7c48c
Author: Ciprian Ciubotariu <ch...@gmx.net>
Authored: Tue Jul 14 18:33:30 2015 +0300
Committer: Ciprian Ciubotariu <ch...@gmx.net>
Committed: Wed Jul 15 16:56:03 2015 +0300

----------------------------------------------------------------------
 .../qa/load/SimulatedJBoss7ServerImpl.java      | 240 --------------
 .../qa/load/SimulatedMySqlNodeImpl.java         | 183 -----------
 .../qa/load/SimulatedNginxControllerImpl.java   | 196 -----------
 .../brooklyn/qa/load/SimulatedTheeTierApp.java  | 140 --------
 .../java/brooklyn/qa/longevity/Monitor.java     | 261 ---------------
 .../brooklyn/qa/longevity/MonitorListener.java  |  35 --
 .../brooklyn/qa/longevity/MonitorPrefs.java     |  54 ---
 .../brooklyn/qa/longevity/MonitorUtils.java     | 329 -------------------
 .../brooklyn/qa/longevity/StatusRecorder.java   | 130 --------
 .../qa/load/SimulatedJBoss7ServerImpl.java      | 240 ++++++++++++++
 .../qa/load/SimulatedMySqlNodeImpl.java         | 183 +++++++++++
 .../qa/load/SimulatedNginxControllerImpl.java   | 196 +++++++++++
 .../brooklyn/qa/load/SimulatedTheeTierApp.java  | 140 ++++++++
 .../apache/brooklyn/qa/longevity/Monitor.java   | 261 +++++++++++++++
 .../brooklyn/qa/longevity/MonitorListener.java  |  35 ++
 .../brooklyn/qa/longevity/MonitorPrefs.java     |  54 +++
 .../brooklyn/qa/longevity/MonitorUtils.java     | 329 +++++++++++++++++++
 .../brooklyn/qa/longevity/StatusRecorder.java   | 130 ++++++++
 .../test/java/brooklyn/qa/load/LoadTest.java    | 243 --------------
 .../brooklyn/qa/longevity/MonitorUtilsTest.java | 165 ----------
 .../webcluster/SinusoidalLoadGenerator.java     |  90 -----
 .../qa/longevity/webcluster/WebClusterApp.java  | 101 ------
 .../org/apache/brooklyn/qa/load/LoadTest.java   | 244 ++++++++++++++
 .../brooklyn/qa/longevity/MonitorUtilsTest.java | 166 ++++++++++
 .../webcluster/SinusoidalLoadGenerator.java     |  90 +++++
 .../qa/longevity/webcluster/WebClusterApp.java  | 101 ++++++
 26 files changed, 2169 insertions(+), 2167 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/brooklyn/qa/load/SimulatedJBoss7ServerImpl.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/brooklyn/qa/load/SimulatedJBoss7ServerImpl.java b/usage/qa/src/main/java/brooklyn/qa/load/SimulatedJBoss7ServerImpl.java
deleted file mode 100644
index 64f7aa5..0000000
--- a/usage/qa/src/main/java/brooklyn/qa/load/SimulatedJBoss7ServerImpl.java
+++ /dev/null
@@ -1,240 +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.qa.load;
-
-import static java.lang.String.format;
-
-import java.util.concurrent.Callable;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.enricher.Enrichers;
-import brooklyn.entity.basic.Attributes;
-import brooklyn.entity.webapp.jboss.JBoss7ServerImpl;
-import brooklyn.entity.webapp.jboss.JBoss7SshDriver;
-import brooklyn.event.feed.function.FunctionFeed;
-import brooklyn.event.feed.function.FunctionPollConfig;
-import brooklyn.event.feed.http.HttpFeed;
-import brooklyn.event.feed.http.HttpPollConfig;
-import brooklyn.event.feed.http.HttpValueFunctions;
-import brooklyn.location.access.BrooklynAccessUtils;
-import brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.guava.Functionals;
-import brooklyn.util.os.Os;
-
-import com.google.common.net.HostAndPort;
-
-/**
- * For simulating various aspects of the JBoss 7 app-server entity.
- *  
- * The use-case for this is that the desired configuration is not always available for testing. 
- * For example, there may be insufficient resources to run 100s of JBoss app-servers, or one 
- * may be experimenting with possible configurations such as use of an external monitoring tool 
- * that is not yet available.
- * 
- * It is then possible to simulate aspects of the behaviour, for performance and load testing purposes. 
- * 
- * There is configuration for:
- * <ul>
- *   <li>{@code simulateEntity}
- *     <ul>
- *       <li>if true, no underlying entity will be started. Instead a sleep 100000 job will be run and monitored.
- *       <li>if false, the underlying entity (i.e. a JBoss app-server) will be started as normal.
- *     </ul>
- *   <li>{@code simulateExternalMonitoring}
- *     <ul>
- *       <li>if true, disables the default monitoring mechanism. Instead, a function will periodically execute 
- *           to set the entity's sensors (as though the values had been obtained from the external monitoring tool).
- *       <li>if false, then:
- *         <ul>
- *           <li>If {@code simulateEntity==true} it will execute comparable commands (e.g. execute a command of the same 
- *               size over ssh or do a comparable number of http GET requests).
- *           <li>If {@code simulateEntity==false} then normal monitoring will be done.
- *         </ul>
- *     </ul>
- *   <li>{@code skipSshOnStart}
- *     <ul>
- *       <li>If true (and if {@code simulateEntity==true}), then no ssh commands will be executed at deploy-time. 
- *           This is useful for speeding up load testing, to get to the desired number of entities.
- *           Should not be set to {@code true} if {@code simulateEntity==false}.
- *       <li>If false, the ssh commands will be executed (based on the value of {@code simulateEntity}.
- *     </ul>
- * </ul>
- */
-public class SimulatedJBoss7ServerImpl extends JBoss7ServerImpl {
-
-    public static final ConfigKey<Boolean> SIMULATE_ENTITY = SimulatedTheeTierApp.SIMULATE_ENTITY;
-    public static final ConfigKey<Boolean> SIMULATE_EXTERNAL_MONITORING = SimulatedTheeTierApp.SIMULATE_EXTERNAL_MONITORING;
-    public static final ConfigKey<Boolean> SKIP_SSH_ON_START = SimulatedTheeTierApp.SKIP_SSH_ON_START;
-    
-    private HttpFeed httpFeed;
-    private FunctionFeed functionFeed;
-    
-    @Override
-    public Class<?> getDriverInterface() {
-        return SimulatedJBoss7SshDriver.class;
-    }
-
-    @Override
-    protected void connectSensors() {
-        boolean simulateEntity = getConfig(SIMULATE_ENTITY);
-        boolean simulateExternalMonitoring = getConfig(SIMULATE_EXTERNAL_MONITORING);
-
-        if (!simulateEntity && !simulateExternalMonitoring) {
-            super.connectSensors();
-            return;
-        }
-        
-        HostAndPort hp = BrooklynAccessUtils.getBrooklynAccessibleAddress(this,
-                getAttribute(MANAGEMENT_HTTP_PORT) + getConfig(PORT_INCREMENT));
-
-        String managementUri = String.format("http://%s:%s/management/subsystem/web/connector/http/read-resource",
-                hp.getHostText(), hp.getPort());
-        setAttribute(MANAGEMENT_URL, managementUri);
-
-        if (simulateExternalMonitoring) {
-            // TODO What would set this normally, if not doing connectServiceUpIsRunning?
-            setAttribute(SERVICE_PROCESS_IS_RUNNING, true);
-        } else {
-            // if simulating entity, then simulate work of periodic HTTP request; TODO but not parsing JSON response
-            String uriToPoll = (simulateEntity) ? "http://localhost:8081" : managementUri;
-            
-            httpFeed = HttpFeed.builder()
-                    .entity(this)
-                    .period(200)
-                    .baseUri(uriToPoll)
-                    .credentials(getConfig(MANAGEMENT_USER), getConfig(MANAGEMENT_PASSWORD))
-                    .poll(new HttpPollConfig<Integer>(MANAGEMENT_STATUS)
-                            .onSuccess(HttpValueFunctions.responseCode()))
-                    .build();
-            
-            // Polls over ssh for whether process is running
-            connectServiceUpIsRunning();
-        }
-        
-        functionFeed = FunctionFeed.builder()
-                .entity(this)
-                .period(5000)
-                .poll(new FunctionPollConfig<Boolean,Boolean>(MANAGEMENT_URL_UP)
-                        .callable(new Callable<Boolean>() {
-                            private int counter = 0;
-                            public Boolean call() {
-                                setAttribute(REQUEST_COUNT, (counter++ % 100));
-                                setAttribute(ERROR_COUNT, (counter++ % 100));
-                                setAttribute(TOTAL_PROCESSING_TIME, (counter++ % 100));
-                                setAttribute(MAX_PROCESSING_TIME, (counter++ % 100));
-                                setAttribute(BYTES_RECEIVED, (long) (counter++ % 100));
-                                setAttribute(BYTES_SENT, (long) (counter++ % 100));
-                                return true;
-                            }}))
-                .build();
-        
-        addEnricher(Enrichers.builder().updatingMap(Attributes.SERVICE_NOT_UP_INDICATORS)
-                .from(MANAGEMENT_URL_UP)
-                .computing(Functionals.ifNotEquals(true).value("Management URL not reachable") )
-                .build());
-    }
-
-    @Override
-    protected void disconnectSensors() {
-        super.disconnectSensors();
-        if (httpFeed != null) httpFeed.stop();
-        if (functionFeed != null) functionFeed.stop();
-    }
-    
-    public static class SimulatedJBoss7SshDriver extends JBoss7SshDriver {
-        public SimulatedJBoss7SshDriver(SimulatedJBoss7ServerImpl entity, SshMachineLocation machine) {
-            super(entity, machine);
-        }
-        
-        @Override
-        public boolean isRunning() {
-            if (entity.getConfig(SKIP_SSH_ON_START)) {
-                return true;
-            } else {
-                return super.isRunning();
-            }
-        }
-
-        @Override
-        public void install() {
-            if (entity.getConfig(SKIP_SSH_ON_START)) {
-                // no-op
-            } else {
-                super.install();
-            }
-        }
-        
-        @Override
-        public void customize() {
-            if (entity.getConfig(SKIP_SSH_ON_START)) {
-                // no-op
-            } else {
-                super.customize();
-            }
-        }
-        
-        @Override
-        public void launch() {
-            if (!entity.getConfig(SIMULATE_ENTITY)) {
-                super.launch();
-                return;
-            }
-            
-            // We wait for evidence of JBoss running because, using
-            // brooklyn.ssh.config.tool.class=brooklyn.util.internal.ssh.cli.SshCliTool,
-            // we saw the ssh session return before the JBoss process was fully running
-            // so the process failed to start.
-            String pidFile = Os.mergePathsUnix(getRunDir(), PID_FILENAME);
-
-            if (entity.getConfig(SKIP_SSH_ON_START)) {
-                // minimal ssh, so that isRunning will subsequently work
-                newScript(MutableMap.of("usePidFile", pidFile), LAUNCHING)
-                        .body.append(
-                                format("nohup sleep 100000 > %s/console 2>&1 < /dev/null &", getRunDir()))
-                        .execute();
-            } else {
-                newScript(MutableMap.of(USE_PID_FILE, false), LAUNCHING)
-                        .body.append(
-                                "export LAUNCH_JBOSS_IN_BACKGROUND=true",
-                                format("export JBOSS_HOME=%s", getExpandedInstallDir()),
-                                format("export JBOSS_PIDFILE=%s/%s", getRunDir(), PID_FILENAME),
-                                format("echo skipping exec of %s/bin/%s.sh ", getExpandedInstallDir(), SERVER_TYPE) +
-                                        format("--server-config %s ", CONFIG_FILE) +
-                                        format("-Djboss.server.base.dir=%s/%s ", getRunDir(), SERVER_TYPE) +
-                                        format("\"-Djboss.server.base.url=file://%s/%s\" ", getRunDir(), SERVER_TYPE) +
-                                        "-Djava.net.preferIPv4Stack=true " +
-                                        "-Djava.net.preferIPv6Addresses=false " +
-                                        format(" >> %s/console 2>&1 </dev/null &", getRunDir()),
-                                format("nohup sleep 100000 > %s/console 2>&1 < /dev/null &", getRunDir()),
-                                format("echo $! > "+pidFile),
-                                format("echo starting > %s/console", getRunDir()),
-                                "for i in {1..10}\n" +
-                                        "do\n" +
-                                        "    grep -i 'starting' "+getRunDir()+"/console && exit\n" +
-                                        "    sleep 1\n" +
-                                        "done\n" +
-                                        "echo \"Couldn't determine if process is running (console output does not contain 'starting'); continuing but may subsequently fail\""
-    
-                            )
-                        .execute();
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/brooklyn/qa/load/SimulatedMySqlNodeImpl.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/brooklyn/qa/load/SimulatedMySqlNodeImpl.java b/usage/qa/src/main/java/brooklyn/qa/load/SimulatedMySqlNodeImpl.java
deleted file mode 100644
index bad437a..0000000
--- a/usage/qa/src/main/java/brooklyn/qa/load/SimulatedMySqlNodeImpl.java
+++ /dev/null
@@ -1,183 +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.qa.load;
-
-import static java.lang.String.format;
-
-import java.util.concurrent.Callable;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver;
-import brooklyn.entity.database.mysql.MySqlNode;
-import brooklyn.entity.database.mysql.MySqlNodeImpl;
-import brooklyn.entity.database.mysql.MySqlSshDriver;
-import brooklyn.entity.software.SshEffectorTasks;
-import brooklyn.event.feed.function.FunctionFeed;
-import brooklyn.event.feed.function.FunctionPollConfig;
-import brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-import brooklyn.util.time.CountdownTimer;
-import brooklyn.util.time.Duration;
-
-/**
- * @see SimulatedJBoss7ServerImpl for description of purpose and configuration options.
- */
-public class SimulatedMySqlNodeImpl extends MySqlNodeImpl {
-
-    public static final ConfigKey<Boolean> SIMULATE_ENTITY = SimulatedTheeTierApp.SIMULATE_ENTITY;
-    public static final ConfigKey<Boolean> SIMULATE_EXTERNAL_MONITORING = SimulatedTheeTierApp.SIMULATE_EXTERNAL_MONITORING;
-    public static final ConfigKey<Boolean> SKIP_SSH_ON_START = SimulatedTheeTierApp.SKIP_SSH_ON_START;
-    
-    private FunctionFeed feed;
-    
-    @Override
-    public Class<?> getDriverInterface() {
-        return SimulatedMySqlSshDriver.class;
-    }
-
-    @Override
-    protected void connectSensors() {
-        boolean simulateExternalMonitoring = getConfig(SIMULATE_EXTERNAL_MONITORING);
-        if (simulateExternalMonitoring) {
-            setAttribute(DATASTORE_URL, String.format("mysql://%s:%s/", getAttribute(HOSTNAME), getAttribute(MYSQL_PORT)));
-            
-            feed = FunctionFeed.builder()
-                    .entity(this)
-                    .period(Duration.FIVE_SECONDS)
-                    .poll(new FunctionPollConfig<Boolean, Boolean>(SERVICE_UP)
-                            .callable(new Callable<Boolean>() {
-                                private int counter = 0;
-                                public Boolean call() {
-                                    setAttribute(QUERIES_PER_SECOND_FROM_MYSQL, (double)(counter++ % 100));
-                                    return true;
-                                }})
-                            .setOnFailureOrException(false))
-                    .build();
-        } else {
-            super.connectSensors();
-        }
-    }
-
-    public static class SimulatedMySqlSshDriver extends MySqlSshDriver {
-
-        private int counter = 0;
-        
-        public SimulatedMySqlSshDriver(SimulatedMySqlNodeImpl entity, SshMachineLocation machine) {
-            super(entity, machine);
-        }
-        
-        // simulate metrics, for if using ssh polling
-        @Override
-        public String getStatusCmd() {
-            if (entity.getConfig(SIMULATE_ENTITY)) {
-                return "echo Uptime: 2427  Threads: 1  Questions: 581  Slow queries: 0  Opens: 53  Flush tables: 1  Open tables: 35  Queries per second avg: "+(counter++ % 100);
-            } else {
-                return super.getStatusCmd();
-            }
-        }
-
-        @Override
-        public void install() {
-            if (entity.getConfig(SKIP_SSH_ON_START)) {
-                // no-op
-            } else {
-                super.install();
-            }
-        }
-        
-        // Not applying creation-script etc, as that requires launching msyqld (so would not scale for single-machine testing)
-        // This is a copy of super.customize, but with the mysqladmin-exec disabled
-        @Override
-        public void customize() {
-            if (!entity.getConfig(SIMULATE_ENTITY)) {
-                super.customize();
-                return;
-            } else if (entity.getConfig(SKIP_SSH_ON_START)) {
-                // no-op
-            } else {
-                copyDatabaseConfigScript();
-    
-                newScript(CUSTOMIZING)
-                    .updateTaskAndFailOnNonZeroResultCode()
-                    .body.append(
-                        "chmod 600 "+getConfigFile(),
-                        getBaseDir()+"/scripts/mysql_install_db "+
-                            "--basedir="+getBaseDir()+" --datadir="+getDataDir()+" "+
-                            "--defaults-file="+getConfigFile())
-                    .execute();
-    
-                // launch, then we will configure it
-                launch();
-    
-                CountdownTimer timer = Duration.seconds(20).countdownTimer();
-                boolean hasCreationScript = copyDatabaseCreationScript();
-                timer.waitForExpiryUnchecked();
-    
-                // DELIBERATELY SKIPPED FOR SCALABILITY TESTING ON SINGLE MACHINE
-                DynamicTasks.queue(
-                    SshEffectorTasks.ssh(
-                        "cd "+getRunDir(),
-                        "echo skipping exec of "+getBaseDir()+"/bin/mysqladmin --defaults-file="+getConfigFile()+" --password= password "+getPassword()
-                    ).summary("setting password"));
-    
-                if (hasCreationScript)
-                    executeScriptFromInstalledFileAsync("creation-script.sql");
-    
-                // not sure necessary to stop then subsequently launch, but seems safest
-                // (if skipping, use a flag in launch to indicate we've just launched it)
-                stop();
-            }
-        }
-
-        @Override
-        public void launch() {
-            if (!entity.getConfig(SIMULATE_ENTITY)) {
-                super.launch();
-                return;
-            }
-            
-            entity.setAttribute(MySqlNode.PID_FILE, getRunDir() + "/" + AbstractSoftwareProcessSshDriver.PID_FILENAME);
-            
-            if (entity.getConfig(SKIP_SSH_ON_START)) {
-                // minimal ssh, so that isRunning will subsequently work
-                newScript(MutableMap.of("usePidFile", true), LAUNCHING)
-                        .body.append(
-                                format("nohup sleep 100000 > %s 2>&1 < /dev/null &", getLogFile()))
-                        .execute();
-            } else {
-                newScript(MutableMap.of("usePidFile", true), LAUNCHING)
-                    .updateTaskAndFailOnNonZeroResultCode()
-                    .body.append(format("echo skipping normal exec of nohup %s/bin/mysqld --defaults-file=%s --user=`whoami` > %s 2>&1 < /dev/null &", getBaseDir(), getConfigFile(), getLogFile()))
-                    .body.append(format("nohup sleep 100000 > %s 2>&1 < /dev/null &", getLogFile()))
-                    .execute();
-            }
-        }
-
-        @Override
-        public ProcessTaskWrapper<Integer> executeScriptFromInstalledFileAsync(String filenameAlreadyInstalledAtServer) {
-            return DynamicTasks.queue(
-                    SshEffectorTasks.ssh(
-                                    "cd "+getRunDir(),
-                                    "echo skipping exec of "+getBaseDir()+"/bin/mysql --defaults-file="+getConfigFile()+" < "+filenameAlreadyInstalledAtServer)
-                            .summary("executing datastore script "+filenameAlreadyInstalledAtServer));
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/brooklyn/qa/load/SimulatedNginxControllerImpl.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/brooklyn/qa/load/SimulatedNginxControllerImpl.java b/usage/qa/src/main/java/brooklyn/qa/load/SimulatedNginxControllerImpl.java
deleted file mode 100644
index e5c40c2..0000000
--- a/usage/qa/src/main/java/brooklyn/qa/load/SimulatedNginxControllerImpl.java
+++ /dev/null
@@ -1,196 +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.qa.load;
-
-import static java.lang.String.format;
-
-import java.net.URI;
-import java.util.Collection;
-import java.util.concurrent.Callable;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.Group;
-import brooklyn.entity.proxy.nginx.NginxControllerImpl;
-import brooklyn.entity.proxy.nginx.NginxSshDriver;
-import brooklyn.entity.proxy.nginx.UrlMapping;
-import brooklyn.event.SensorEvent;
-import brooklyn.event.SensorEventListener;
-import brooklyn.event.feed.ConfigToAttributes;
-import brooklyn.event.feed.function.FunctionFeed;
-import brooklyn.event.feed.function.FunctionPollConfig;
-import brooklyn.event.feed.http.HttpFeed;
-import brooklyn.event.feed.http.HttpPollConfig;
-import brooklyn.location.basic.SshMachineLocation;
-import brooklyn.policy.PolicySpec;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.net.Networking;
-
-import com.google.common.base.Functions;
-
-/**
- * @see SimulatedJBoss7ServerImpl for description of purpose and configuration options.
- */
-public class SimulatedNginxControllerImpl extends NginxControllerImpl {
-
-    public static final ConfigKey<Boolean> SIMULATE_ENTITY = SimulatedTheeTierApp.SIMULATE_ENTITY;
-    public static final ConfigKey<Boolean> SIMULATE_EXTERNAL_MONITORING = SimulatedTheeTierApp.SIMULATE_EXTERNAL_MONITORING;
-    public static final ConfigKey<Boolean> SKIP_SSH_ON_START = SimulatedTheeTierApp.SKIP_SSH_ON_START;
-    
-    private HttpFeed httpFeed;
-    private FunctionFeed functionFeed;
-    
-    @Override
-    public Class<?> getDriverInterface() {
-        return SimulatedNginxSshDriver.class;
-    }
-
-    @Override
-    public void connectSensors() {
-        boolean simulateEntity = getConfig(SIMULATE_ENTITY);
-        boolean simulateExternalMonitoring = getConfig(SIMULATE_EXTERNAL_MONITORING);
-
-        if (!simulateEntity && !simulateExternalMonitoring) {
-            super.connectSensors();
-            return;
-        }
-
-        // From AbstractController.connectSensors
-        if (getUrl()==null) {
-            setAttribute(MAIN_URI, URI.create(inferUrl()));
-            setAttribute(ROOT_URL, inferUrl());
-        }
-        addServerPoolMemberTrackingPolicy();
-
-        // From NginxController.connectSensors
-        ConfigToAttributes.apply(this);
-
-        if (!simulateExternalMonitoring) {
-            // if simulating entity, then simulate work of periodic HTTP request; TODO but not parsing JSON response
-            String uriToPoll = (simulateEntity) ? "http://localhost:8081" : getAttribute(MAIN_URI).toString();
-            
-            httpFeed = HttpFeed.builder()
-                    .entity(this)
-                    .period(getConfig(HTTP_POLL_PERIOD))
-                    .baseUri(uriToPoll)
-                    .poll(new HttpPollConfig<Boolean>(SERVICE_UP)
-                            .onSuccess(Functions.constant(true))
-                            .onFailureOrException(Functions.constant(true)))
-                    .build();
-        }
-        
-        functionFeed = FunctionFeed.builder()
-                .entity(this)
-                .period(getConfig(HTTP_POLL_PERIOD))
-                .poll(new FunctionPollConfig<Boolean,Boolean>(SERVICE_UP)
-                        .callable(new Callable<Boolean>() {
-                            public Boolean call() {
-                                return true;
-                            }}))
-                .build();
-
-        // Can guarantee that parent/managementContext has been set
-        Group urlMappings = getConfig(URL_MAPPINGS);
-        if (urlMappings != null) {
-            // Listen to the targets of each url-mapping changing
-            subscribeToMembers(urlMappings, UrlMapping.TARGET_ADDRESSES, new SensorEventListener<Collection<String>>() {
-                    @Override public void onEvent(SensorEvent<Collection<String>> event) {
-                        updateNeeded();
-                    }
-                });
-
-            // Listen to url-mappings being added and removed
-            urlMappingsMemberTrackerPolicy = addPolicy(PolicySpec.create(UrlMappingsMemberTrackerPolicy.class)
-                    .configure("group", urlMappings));
-        }
-    }
-
-    @Override
-    protected void disconnectSensors() {
-        super.disconnectSensors();
-        if (httpFeed != null) httpFeed.stop();
-        if (functionFeed != null) functionFeed.stop();
-    }
-
-    public static class SimulatedNginxSshDriver extends NginxSshDriver {
-        public SimulatedNginxSshDriver(SimulatedNginxControllerImpl entity, SshMachineLocation machine) {
-            super(entity, machine);
-        }
-
-        @Override
-        public void install() {
-            if (entity.getConfig(SKIP_SSH_ON_START)) {
-                // no-op
-            } else {
-                super.install();
-            }
-        }
-        
-        @Override
-        public void customize() {
-            if (entity.getConfig(SKIP_SSH_ON_START)) {
-                // no-op
-            } else {
-                super.customize();
-            }
-        }
-        
-        @Override
-        public void launch() {
-            if (!entity.getConfig(SIMULATE_ENTITY)) {
-                super.launch();
-                return;
-            }
-            
-            Networking.checkPortsValid(MutableMap.of("httpPort", getPort()));
-
-            if (entity.getConfig(SKIP_SSH_ON_START)) {
-                // minimal ssh, so that isRunning will subsequently work
-                newScript(MutableMap.of("usePidFile", getPidFile()), LAUNCHING)
-                        .body.append(
-                                format("mkdir -p %s/logs", getRunDir()),
-                                format("nohup sleep 100000 > %s 2>&1 < /dev/null &", getLogFileLocation()))
-                        .execute();
-            } else {
-                newScript(MutableMap.of("usePidFile", false), LAUNCHING)
-                        .body.append(
-                                format("cd %s", getRunDir()),
-                                "echo skipping exec of requireExecutable ./sbin/nginx",
-                                sudoBashCIfPrivilegedPort(getPort(), format(
-                                        "echo skipping exec of nohup ./sbin/nginx -p %s/ -c conf/server.conf > %s 2>&1 &", getRunDir(), getLogFileLocation())),
-                                format("nohup sleep 100000 > %s 2>&1 < /dev/null &", getLogFileLocation()),
-                                format("echo $! > "+getPidFile()),
-                                format("for i in {1..10}\n" +
-                                        "do\n" +
-                                        "    test -f %1$s && ps -p `cat %1$s` && exit\n" +
-                                        "    sleep 1\n" +
-                                        "done\n" +
-                                        "echo \"No explicit error launching nginx but couldn't find process by pid; continuing but may subsequently fail\"\n" +
-                                        "cat %2$s | tee /dev/stderr",
-                                        getPidFile(), getLogFileLocation()))
-                        .execute();
-            }
-        }
-
-        // Use pid file, because just simulating the run of nginx
-        @Override
-        public void stop() {
-            newScript(MutableMap.of("usePidFile", getPidFile()), STOPPING).execute();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/brooklyn/qa/load/SimulatedTheeTierApp.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/brooklyn/qa/load/SimulatedTheeTierApp.java b/usage/qa/src/main/java/brooklyn/qa/load/SimulatedTheeTierApp.java
deleted file mode 100644
index 6f95209..0000000
--- a/usage/qa/src/main/java/brooklyn/qa/load/SimulatedTheeTierApp.java
+++ /dev/null
@@ -1,140 +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.qa.load;
-
-import static brooklyn.event.basic.DependentConfiguration.attributeWhenReady;
-import static brooklyn.event.basic.DependentConfiguration.formatString;
-
-import java.util.Collection;
-import java.util.List;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.enricher.Enrichers;
-import brooklyn.enricher.HttpLatencyDetector;
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.Attributes;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.StartableApplication;
-import brooklyn.entity.database.mysql.MySqlNode;
-import brooklyn.entity.group.DynamicCluster;
-import brooklyn.entity.java.JavaEntityMethods;
-import brooklyn.entity.proxy.nginx.NginxController;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.trait.Startable;
-import brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
-import brooklyn.entity.webapp.DynamicWebAppCluster;
-import brooklyn.entity.webapp.JavaWebAppService;
-import brooklyn.entity.webapp.WebAppService;
-import brooklyn.entity.webapp.WebAppServiceConstants;
-import brooklyn.entity.webapp.jboss.JBoss7Server;
-import brooklyn.launcher.BrooklynLauncher;
-import brooklyn.location.basic.PortRanges;
-import brooklyn.policy.autoscaling.AutoScalerPolicy;
-import brooklyn.util.CommandLineUtil;
-import brooklyn.util.collections.MutableSet;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-
-/**
- * A 3-tier app where all components are just "simulated" - they don't actually run 
- * real app-servers or databases, instead just executing a "sleep" command to simulate 
- * the running process.
- * 
- * This is useful for load testing, where we want to test the performance of Brooklyn
- * rather than the ability to host many running app-servers.
- * 
- * The app is based on WebClusterDatabaseExampleApp
- * 
- * @see SimulatedJBoss7ServerImpl for description of purpose and configuration options.
- */
-public class SimulatedTheeTierApp extends AbstractApplication {
-
-    public static final ConfigKey<Boolean> SIMULATE_ENTITY = ConfigKeys.newBooleanConfigKey("simulateEntity", "", true);
-    
-    public static final ConfigKey<Boolean> SIMULATE_EXTERNAL_MONITORING = ConfigKeys.newBooleanConfigKey("simulateExternalMonitoring", "", true);
-
-    public static final ConfigKey<Boolean> SKIP_SSH_ON_START = ConfigKeys.newBooleanConfigKey("skipSshOnStart", "", false);
-
-    public static final String WAR_PATH = "classpath://hello-world.war";
-    public static final String DB_TABLE = "visitors";
-    public static final String DB_USERNAME = "brooklyn";
-    public static final String DB_PASSWORD = "br00k11n";
-    public static final boolean USE_HTTPS = false;
-    
-    @Override
-    public void init() {
-        MySqlNode mysql = addChild(
-                EntitySpec.create(MySqlNode.class)
-                .impl(SimulatedMySqlNodeImpl.class));
-
-        ControlledDynamicWebAppCluster web = addChild(
-                EntitySpec.create(ControlledDynamicWebAppCluster.class)
-                .configure(ControlledDynamicWebAppCluster.MEMBER_SPEC, EntitySpec.create(JBoss7Server.class).impl(SimulatedJBoss7ServerImpl.class))
-                .configure(ControlledDynamicWebAppCluster.CONTROLLER_SPEC, EntitySpec.create(NginxController.class).impl(SimulatedNginxControllerImpl.class))
-                        .configure(WebAppService.HTTP_PORT, PortRanges.fromString("8080+"))
-                        .configure(JavaWebAppService.ROOT_WAR, WAR_PATH)
-                        .configure(JavaEntityMethods.javaSysProp("brooklyn.example.db.url"), 
-                                formatString("jdbc:%s%s?user=%s\\&password=%s", 
-                                        attributeWhenReady(mysql, MySqlNode.DATASTORE_URL), DB_TABLE, DB_USERNAME, DB_PASSWORD))
-                        .configure(DynamicCluster.INITIAL_SIZE, 2)
-                        .configure(WebAppService.ENABLED_PROTOCOLS, ImmutableSet.of(USE_HTTPS ? "https" : "http")) );
-
-        web.getCluster().addPolicy(AutoScalerPolicy.builder().
-                metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW_PER_NODE).
-                metricRange(10, 100).
-                sizeRange(2, 5).
-                build());
-
-        addEnricher(Enrichers.builder()
-                .propagating(Attributes.MAIN_URI, WebAppServiceConstants.ROOT_URL,
-                        DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW,
-                        HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW)
-                .from(web)
-                .build());
-        
-        addEnricher(Enrichers.builder()
-                .aggregating(Startable.SERVICE_UP)
-                .publishing(Startable.SERVICE_UP)
-                .fromHardcodedProducers(ImmutableList.of(web, mysql))
-                .computing(new Function<Collection<Boolean>, Boolean>() {
-                    @Override public Boolean apply(Collection<Boolean> input) {
-                        return input != null && input.size() == 2 && MutableSet.copyOf(input).equals(ImmutableSet.of(true));
-                    }})
-                .build());
-    }
-    
-    public static void main(String[] argv) {
-        List<String> args = Lists.newArrayList(argv);
-        String port =  CommandLineUtil.getCommandLineOption(args, "--port", "8081+");
-        String location = CommandLineUtil.getCommandLineOption(args, "--location", "localhost");
-
-        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
-                 .application(EntitySpec.create(StartableApplication.class, SimulatedTheeTierApp.class)
-                         .displayName("Brooklyn WebApp Cluster with Database example"))
-                 .webconsolePort(port)
-                 .location(location)
-                 .start();
-             
-        Entities.dumpInfo(launcher.getApplications());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/brooklyn/qa/longevity/Monitor.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/brooklyn/qa/longevity/Monitor.java b/usage/qa/src/main/java/brooklyn/qa/longevity/Monitor.java
deleted file mode 100644
index 36e9f55..0000000
--- a/usage/qa/src/main/java/brooklyn/qa/longevity/Monitor.java
+++ /dev/null
@@ -1,261 +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.qa.longevity;
-
-import static brooklyn.qa.longevity.StatusRecorder.Factory.chain;
-import static brooklyn.qa.longevity.StatusRecorder.Factory.noop;
-import static brooklyn.qa.longevity.StatusRecorder.Factory.toFile;
-import static brooklyn.qa.longevity.StatusRecorder.Factory.toLog;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import joptsimple.OptionParser;
-import joptsimple.OptionSet;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.collections.TimeWindowedList;
-import brooklyn.util.collections.TimestampedValue;
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Charsets;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Range;
-import com.google.common.io.Files;
-
-public class Monitor {
-
-    private static final Logger LOG = LoggerFactory.getLogger(Monitor.class);
-    
-    private static final int checkPeriodMs = 1000;
-
-    private static final OptionParser parser = new OptionParser() {
-        {
-            acceptsAll(ImmutableList.of("help", "?", "h"), "show help");
-            accepts("webUrl", "Web-app url")
-                    .withRequiredArg().ofType(URL.class);
-            accepts("brooklynPid", "Brooklyn pid")
-                    .withRequiredArg().ofType(Integer.class);
-            accepts("logFile", "Brooklyn log file")
-                    .withRequiredArg().ofType(File.class);
-            accepts("logGrep", "Grep in log file (defaults to 'SEVERE|ERROR|WARN|Exception|Error'")
-                    .withRequiredArg().ofType(String.class);
-            accepts("logGrepExclusionsFile", "File of expressions to be ignored in log file")
-                    .withRequiredArg().ofType(File.class);
-            accepts("webProcesses", "Name (for `ps ax | grep` of web-processes")
-                    .withRequiredArg().ofType(String.class);
-            accepts("numWebProcesses", "Number of web-processes expected (e.g. 1 or 1-3)")
-                    .withRequiredArg().ofType(String.class);
-            accepts("webProcessesCyclingPeriod", "The period (in seconds) for cycling through the range of numWebProcesses")
-                    .withRequiredArg().ofType(Integer.class);
-            accepts("outFile", "File to write monitor status info")
-                    .withRequiredArg().ofType(File.class);
-            accepts("abortOnError", "Exit the JVM on error, with exit code 1")
-                    .withRequiredArg().ofType(Boolean.class);
-        }
-    };
-
-    public static void main(String[] argv) throws InterruptedException, IOException {
-        OptionSet options = parse(argv);
-
-        if (options == null || options.has("help")) {
-            parser.printHelpOn(System.out);
-            System.exit(0);
-        }
-
-        MonitorPrefs prefs = new MonitorPrefs();
-        prefs.webUrl = options.hasArgument("webUrl") ? (URL) options.valueOf("webUrl") : null;
-        prefs.brooklynPid = options.hasArgument("brooklynPid") ? (Integer) options.valueOf("brooklynPid") : -1;
-        prefs.logFile = options.hasArgument("logFile") ? (File) options.valueOf("logFile") : null;
-        prefs.logGrep = options.hasArgument("logGrep") ? (String) options.valueOf("logGrep") : "SEVERE|ERROR|WARN|Exception|Error";
-        prefs.logGrepExclusionsFile = options.hasArgument("logGrepExclusionsFile") ? (File) options.valueOf("logGrepExclusionsFile") : null;
-        prefs.webProcessesRegex = options.hasArgument("webProcesses") ? (String) options.valueOf("webProcesses") : null;
-        prefs.numWebProcesses = options.hasArgument("numWebProcesses") ? parseRange((String) options.valueOf("numWebProcesses")) : null;
-        prefs.webProcessesCyclingPeriod = options.hasArgument("webProcessesCyclingPeriod") ? (Integer) options.valueOf("webProcessesCyclingPeriod") : -1;
-        prefs.outFile = options.hasArgument("outFile") ? (File) options.valueOf("outFile") : null;
-        prefs.abortOnError = options.hasArgument("abortOnError") ? (Boolean) options.valueOf("abortOnError") : false;
-        Monitor main = new Monitor(prefs, MonitorListener.NOOP);
-        main.start();
-    }
-
-    private static Range<Integer> parseRange(String range) {
-        if (range.contains("-")) {
-            String[] parts = range.split("-");
-            return Range.closed(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
-        } else {
-            return Range.singleton(Integer.parseInt(range));
-        }
-    }
-    
-    private static OptionSet parse(String...argv) {
-        try {
-            return parser.parse(argv);
-        } catch (Exception e) {
-            System.out.println("Error in parsing options: " + e.getMessage());
-            return null;
-        }
-    }
-
-    private final MonitorPrefs prefs;
-    private final StatusRecorder recorder;
-    private final MonitorListener listener;
-    
-    public Monitor(MonitorPrefs prefs, MonitorListener listener) {
-        this.prefs = prefs;
-        this.listener = listener;
-        this.recorder = chain(toLog(LOG), (prefs.outFile != null ? toFile(prefs.outFile) : noop()));
-    }
-
-    private void start() throws IOException {
-        LOG.info("Monitoring: "+prefs);
-        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
-
-        final AtomicReference<List<String>> previousLogLines = new AtomicReference<List<String>>(Collections.<String>emptyList());
-        final TimeWindowedList<Integer> numWebProcessesHistory = new TimeWindowedList<Integer>(
-                ImmutableMap.of("timePeriod", Duration.seconds(prefs.webProcessesCyclingPeriod), "minExpiredVals", 1));
-        final Set<String> logGrepExclusions = ImmutableSet.copyOf(Files.readLines(prefs.logGrepExclusionsFile, Charsets.UTF_8));
-        
-        executor.scheduleAtFixedRate(new Runnable() {
-                @Override public void run() {
-                    StatusRecorder.Record record = new StatusRecorder.Record();
-                    StringBuilder failureMsg = new StringBuilder();
-                    try {
-                        if (prefs.brooklynPid > 0) {
-                            boolean pidRunning = MonitorUtils.isPidRunning(prefs.brooklynPid, "java");
-                            MonitorUtils.MemoryUsage memoryUsage = MonitorUtils.getMemoryUsage(prefs.brooklynPid, ".*brooklyn.*", 1000);
-                            record.put("pidRunning", pidRunning);
-                            record.put("totalMemoryBytes", memoryUsage.getTotalMemoryBytes());
-                            record.put("totalMemoryInstances", memoryUsage.getTotalInstances());
-                            record.put("instanceCounts", memoryUsage.getInstanceCounts());
-                            
-                            if (!pidRunning) {
-                                failureMsg.append("pid "+prefs.brooklynPid+" is not running"+"\n");
-                            }
-                        }
-                        if (prefs.webUrl != null) {
-                            boolean webUrlUp = MonitorUtils.isUrlUp(prefs.webUrl);
-                            record.put("webUrlUp", webUrlUp);
-                            
-                            if (!webUrlUp) {
-                                failureMsg.append("web URL "+prefs.webUrl+" is not available"+"\n");
-                            }
-                        }
-                        if (prefs.logFile != null) {
-                            List<String> logLines = MonitorUtils.searchLog(prefs.logFile, prefs.logGrep, logGrepExclusions);
-                            List<String> newLogLines = getAdditions(previousLogLines.get(), logLines);
-                            previousLogLines.set(logLines);
-                            record.put("logLines", newLogLines);
-                            
-                            if (newLogLines.size() > 0) {
-                                failureMsg.append("Log contains warnings/errors: "+newLogLines+"\n");
-                            }
-                        }
-                        if (prefs.webProcessesRegex != null) {
-                            List<Integer> pids = MonitorUtils.getRunningPids(prefs.webProcessesRegex, "--webProcesses");
-                            pids.remove((Object)MonitorUtils.findOwnPid());
-                            
-                            record.put("webPids", pids);
-                            record.put("numWebPids", pids.size());
-                            numWebProcessesHistory.add(pids.size());
-                            
-                            if (prefs.numWebProcesses != null) {
-                                boolean numWebPidsInRange = prefs.numWebProcesses.apply(pids.size());
-                                record.put("numWebPidsInRange", numWebPidsInRange);
-                                
-                                if (!numWebPidsInRange) {
-                                    failureMsg.append("num web processes out-of-range: pids="+pids+"; size="+pids.size()+"; expected="+prefs.numWebProcesses);
-                                }
-                            
-                                if (prefs.webProcessesCyclingPeriod > 0) {
-                                    List<TimestampedValue<Integer>> values = numWebProcessesHistory.getValues();
-                                    long valuesTimeRange = (values.get(values.size()-1).getTimestamp() - values.get(0).getTimestamp());
-                                    if (values.size() > 0 && valuesTimeRange > SECONDS.toMillis(prefs.webProcessesCyclingPeriod)) {
-                                        int min = -1;
-                                        int max = -1;
-                                        for (TimestampedValue<Integer> val : values) {
-                                            min = (min < 0) ? val.getValue() : Math.min(val.getValue(), min);
-                                            max = Math.max(val.getValue(), max);
-                                        }
-                                        record.put("minWebSizeInPeriod", min);
-                                        record.put("maxWebSizeInPeriod", max);
-                                        
-                                        if (min > prefs.numWebProcesses.lowerEndpoint() || max < prefs.numWebProcesses.upperEndpoint()) {
-                                            failureMsg.append("num web processes not increasing/decreasing correctly: " +
-                                                    "pids="+pids+"; size="+pids.size()+"; cyclePeriod="+prefs.webProcessesCyclingPeriod+
-                                                    "; expectedRange="+prefs.numWebProcesses+"; min="+min+"; max="+max+"; history="+values);
-                                        }
-                                    } else {
-                                        int numVals = values.size();
-                                        long startTime = (numVals > 0) ? values.get(0).getTimestamp() : 0;
-                                        long endTime = (numVals > 0) ? values.get(values.size()-1).getTimestamp() : 0;
-                                        LOG.info("Insufficient vals in time-window to determine cycling behaviour over period ("+prefs.webProcessesCyclingPeriod+"secs): "+
-                                                "numVals="+numVals+"; startTime="+startTime+"; endTime="+endTime+"; periodCovered="+(endTime-startTime)/1000);
-                                    }
-                                }
-                            }
-                        }
-                        
-                    } catch (Throwable t) {
-                        LOG.error("Error during periodic checks", t);
-                        throw Throwables.propagate(t);
-                    }
-                    
-                    try {
-                        recorder.record(record);
-                        listener.onRecord(record);
-                        
-                        if (failureMsg.length() > 0) {
-                            listener.onFailure(record, failureMsg.toString());
-                            
-                            if (prefs.abortOnError) {
-                                LOG.error("Aborting on error: "+failureMsg);
-                                System.exit(1);
-                            }
-                        }
-                        
-                    } catch (Throwable t) {
-                        LOG.warn("Error recording monitor info ("+record+")", t);
-                        throw Throwables.propagate(t);
-                    }
-                }
-            }, 0, checkPeriodMs, TimeUnit.MILLISECONDS);
-    }
-    
-    // TODO What is the guava equivalent? Don't want Set.difference, because duplicates/ordered.
-    private static List<String> getAdditions(List<String> prev, List<String> next) {
-        List<String> result = Lists.newArrayList(next);
-        result.removeAll(prev);
-        return result;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorListener.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorListener.java b/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorListener.java
deleted file mode 100644
index b0a0678..0000000
--- a/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorListener.java
+++ /dev/null
@@ -1,35 +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.qa.longevity;
-
-import brooklyn.qa.longevity.StatusRecorder.Record;
-
-public interface MonitorListener {
-
-    public static final MonitorListener NOOP = new MonitorListener() {
-        @Override public void onRecord(Record record) {
-        }
-        @Override public void onFailure(Record record, String msg) {
-        }
-    };
-    
-    public void onRecord(Record record);
-    
-    public void onFailure(Record record, String msg);
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorPrefs.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorPrefs.java b/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorPrefs.java
deleted file mode 100644
index 3883e93..0000000
--- a/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorPrefs.java
+++ /dev/null
@@ -1,54 +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.qa.longevity;
-
-import java.io.File;
-import java.net.URL;
-
-import com.google.common.base.Objects;
-import com.google.common.collect.Range;
-
-public class MonitorPrefs {
-
-    public URL webUrl;
-    public int brooklynPid;
-    public File logFile;
-    public String logGrep;
-    public File logGrepExclusionsFile;
-    public String webProcessesRegex;
-    public Range<Integer> numWebProcesses;
-    public int webProcessesCyclingPeriod;
-    public File outFile;
-    public boolean abortOnError;
-    
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(this)
-                .add("webUrl", webUrl)
-                .add("brooklynPid", brooklynPid)
-                .add("logFile", logFile)
-                .add("logGrep", logGrep)
-                .add("logGrepExclusionsFile", logGrepExclusionsFile)
-                .add("outFile", outFile)
-                .add("webProcessesRegex", webProcessesRegex)
-                .add("numWebProcesses", numWebProcesses)
-                .add("webProcessesCyclingPeriod", webProcessesCyclingPeriod)
-                .toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorUtils.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorUtils.java b/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorUtils.java
deleted file mode 100644
index 290a555..0000000
--- a/usage/qa/src/main/java/brooklyn/qa/longevity/MonitorUtils.java
+++ /dev/null
@@ -1,329 +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.qa.longevity;
-
-import static com.google.common.base.Strings.isNullOrEmpty;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
-import brooklyn.util.stream.StreamGobbler;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.google.common.io.ByteStreams;
-
-public class MonitorUtils {
-
-    private static final Logger LOG = LoggerFactory.getLogger(MonitorUtils.class);
-
-    private static volatile int ownPid = -1;
-
-    /**
-     * Confirm can read from URL.
-     *
-     * @param url
-     */
-    public static boolean isUrlUp(URL url) {
-        try {
-            HttpToolResponse result = HttpTool.httpGet(
-                    HttpTool.httpClientBuilder().trustAll().build(), 
-                    URI.create(url.toString()), 
-                    ImmutableMap.<String,String>of());
-            int statuscode = result.getResponseCode();
-
-            if (statuscode != 200) {
-                LOG.info("Error reading URL {}: {}, {}", new Object[]{url, statuscode, result.getReasonPhrase()});
-                return false;
-            } else {
-                return true;
-            }
-        } catch (Exception e) {
-            LOG.info("Error reading URL {}: {}", url, e);
-            return false;
-        }
-    }
-
-    public static boolean isPidRunning(int pid) {
-        return isPidRunning(pid, null);
-    }
-
-    /**
-     * Confirm the given pid is running, and that the the process matches the given regex.
-     *
-     * @param pid
-     * @param regex
-     */
-    public static boolean isPidRunning(int pid, String regex) {
-        Process process = exec("ps -p " + pid);
-        String out = waitFor(process);
-         if (process.exitValue() > 0) {
-            String err = toString(process.getErrorStream());
-            LOG.info(String.format("pid %s not running: %s", pid, err));
-            return false;
-        }
-
-        if (regex != null) {
-            String regex2 = "^\\s*" + pid + ".*" + regex;
-            boolean found = false;
-            for (String line : out.split("\n")) {
-                if (hasAtLeastOneMatch(line, regex2)) {
-                    found = true;
-                    break;
-                }
-            }
-
-            if (!found) {
-                String txt = toString(process.getInputStream());
-                LOG.info("process did not match regular expression: "+txt);
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    private static boolean hasAtLeastOneMatch(String line, String regex) {
-        return Pattern.matches(".*"+regex+".*", line);
-    }
-
-    private static String toString(InputStream in){
-        try {
-            byte[] bytes = ByteStreams.toByteArray(in);
-            return new String(bytes);
-        } catch (IOException e) {
-            throw Throwables.propagate(e);
-        }
-
-    }
-
-    public static List<Integer> getRunningPids(String regex) {
-        return getRunningPids(regex, null);
-    }
-
-    /**
-     * Confirm the given pid is running, and that the the process matches the given regex.
-     *
-     * @param regex
-     * @param excludingRegex
-     */
-    public static List<Integer> getRunningPids(String regex, String excludingRegex) {
-        Process process = exec("ps ax");
-        String out = waitFor(process);
-
-        List<Integer> result = new LinkedList<Integer>();
-        for (String line : out.split("\n")) {
-            if (excludingRegex != null && hasAtLeastOneMatch(line, excludingRegex)) {
-                continue;
-            }
-            if (hasAtLeastOneMatch(line, regex)) {
-                String[] linesplit = line.trim().split("\\s+");
-                result.add(Integer.parseInt(linesplit[0]));
-            }
-        }
-        return result;
-    }
-
-    public static MemoryUsage getMemoryUsage(int pid){
-          return getMemoryUsage(pid, null,0);
-    }
-
-    /**
-     * @param pid
-     */
-    public static MemoryUsage getMemoryUsage(int pid, String clazzRegexOfInterest, int minInstancesOfInterest) {
-        Process process = exec(String.format("jmap -histo %s", pid));
-        String out = waitFor(process);
-
-        Map<String, Integer> instanceCounts = Maps.newLinkedHashMap();
-        long totalInstances=0;
-        long totalMemoryBytes=0;
-
-        for (String line : out.split("\n")) {
-            if (clazzRegexOfInterest!=null && hasAtLeastOneMatch(line, clazzRegexOfInterest)) {
-                // Format is:
-                //   num     #instances         #bytes  class name
-                //   1:           43506        8047096  example.MyClazz
-
-                String[] parts = line.trim().split("\\s+");
-                String clazz = parts[3];
-                int instanceCount = Integer.parseInt(parts[1]);
-                if (instanceCount >= minInstancesOfInterest) {
-                    instanceCounts.put(clazz, instanceCount);
-                }
-            }
-            if (hasAtLeastOneMatch(line, "^Total.*")) {
-                String[] parts = line.split("\\s+");
-                totalInstances = Long.parseLong(parts[1]);
-                totalMemoryBytes = Long.parseLong(parts[2]);
-            }
-        }
-
-        return new MemoryUsage(totalInstances, totalMemoryBytes, instanceCounts);
-    }
-
-    public static class MemoryUsage {
-        final long totalInstances;
-        final long totalMemoryBytes;
-        final Map<String, Integer> instanceCounts;
-
-        MemoryUsage(long totalInstances, long totalMemoryBytes, Map<String, Integer> instanceCounts) {
-            this.totalInstances = totalInstances;
-            this.totalMemoryBytes = totalMemoryBytes;
-            this.instanceCounts = instanceCounts;
-        }
-
-        public String toString() {
-            return Objects.toStringHelper(this)
-                    .add("totalInstances", totalInstances)
-                    .add("totalMemoryBytes", totalMemoryBytes)
-                    .add("instanceCounts", instanceCounts)
-                    .toString();
-        }
-
-        public long getTotalInstances() {
-            return totalInstances;
-        }
-
-        public long getTotalMemoryBytes() {
-            return totalMemoryBytes;
-        }
-
-        public Map<String, Integer> getInstanceCounts() {
-            return instanceCounts;
-        }
-    }
-
-    public static List<String> searchLog(File file, String grepOfInterest) {
-        return searchLog(file, grepOfInterest, new LinkedHashSet<String>());
-    }
-
-    /**
-     * Find lines in the given file that match given given regex.
-     *
-     * @param file
-     * @param grepOfInterest
-     */
-    public static List<String> searchLog(File file, String grepOfInterest, Set<String> grepExclusions) {
-        Process process = exec(String.format("grep -E %s %s", grepOfInterest, file.getAbsoluteFile()));
-        String out = waitFor(process);
-
-        // TODO Annoying that String.split() returns size 1 when empty string; lookup javadoc when back online...
-        if (out.length() == 0) return Collections.<String>emptyList();
-
-        List<String> result = new ArrayList<String>();
-        for (String line : out.trim().split("\n")) {
-            boolean excluded = false;
-            for (String exclusion : grepExclusions) {
-                if (!isNullOrEmpty(exclusion) && hasAtLeastOneMatch(line, exclusion)) {
-                    excluded = true;
-                }
-            }
-            if (!excluded) {
-                result.add(line);
-            }
-        }
-        return result;
-    }
-
-    public static Process exec(String cmd) {
-        LOG.info("executing cmd: " + cmd);
-
-        try {
-            return Runtime.getRuntime().exec(cmd);
-        } catch (IOException e) {
-            throw Throwables.propagate(e);
-        }
-    }
-
-    public static class ProcessHasStderr extends IllegalStateException {
-        private static final long serialVersionUID = -937871002993888405L;
-        
-        byte[] stderrBytes;
-        public ProcessHasStderr(byte[] stderrBytes) {
-            this("Process printed to stderr: " + new String(stderrBytes), stderrBytes);
-        }
-        public ProcessHasStderr(String message, byte[] stderrBytes) {
-            super(message);
-            this.stderrBytes = stderrBytes;
-        }
-    }
-    
-    /**
-     * Waits for the given process to complete, consuming its stdout and returning it as a string.
-     * If there is any output on stderr an exception will be thrown.
-     */
-    public static String waitFor(Process process) {
-        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
-        @SuppressWarnings("resource") //Closeable doesn't seem appropriate for StreamGobbler since it isn't expected to be called every time
-        StreamGobbler gobblerOut = new StreamGobbler(process.getInputStream(), bytesOut, null);
-        gobblerOut.start();
-
-        ByteArrayOutputStream bytesErr = new ByteArrayOutputStream();
-        @SuppressWarnings("resource")
-        StreamGobbler gobblerErr = new StreamGobbler(process.getErrorStream(), bytesErr, null);
-        gobblerErr.start();
-
-        try {
-            process.waitFor();
-            gobblerOut.blockUntilFinished();
-            gobblerErr.blockUntilFinished();
-
-            if (bytesErr.size() > 0) {
-                throw new ProcessHasStderr(bytesErr.toByteArray());
-            }
-
-            return new String(bytesOut.toByteArray());
-        } catch (Exception e) {
-            throw Throwables.propagate(e);
-        } finally {
-            if (gobblerOut.isAlive()) gobblerOut.interrupt();
-            if (gobblerErr.isAlive()) gobblerErr.interrupt();
-        }
-    }
-
-    public static int findOwnPid() throws IOException {
-        if (ownPid >= 0) return ownPid;
-
-        String[] cmd = new String[]{"bash", "-c", "echo $PPID"};
-        Process process = Runtime.getRuntime().exec(cmd);
-        String out = MonitorUtils.waitFor(process);
-        ownPid = Integer.parseInt(out.trim());
-        return ownPid;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/brooklyn/qa/longevity/StatusRecorder.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/brooklyn/qa/longevity/StatusRecorder.java b/usage/qa/src/main/java/brooklyn/qa/longevity/StatusRecorder.java
deleted file mode 100644
index 5664a2d..0000000
--- a/usage/qa/src/main/java/brooklyn/qa/longevity/StatusRecorder.java
+++ /dev/null
@@ -1,130 +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.qa.longevity;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Map;
-
-import org.slf4j.Logger;
-
-import com.google.common.base.Charsets;
-import com.google.common.collect.Maps;
-import com.google.common.io.Files;
-
-public interface StatusRecorder {
-
-    public void record(Record record) throws IOException;
-    
-    public static class Factory {
-        public static final StatusRecorder NOOP = new StatusRecorder() {
-            @Override public void record(Record record) {}
-        };
-        
-        public static StatusRecorder noop() {
-            return NOOP;
-        }
-        public static StatusRecorder toFile(File outFile) {
-            return new FileBasedStatusRecorder(outFile);
-        }
-        public static StatusRecorder toSysout() {
-            return new SysoutBasedStatusRecorder();
-        }
-        public static StatusRecorder toLog(Logger log) {
-            return new LogBasedStatusRecorder(log);
-        }
-        public static StatusRecorder chain(StatusRecorder...recorders) {
-            return new ChainingStatusRecorder(recorders);
-        }
-    }
-    
-    public static class Record {
-        private final Map<String,Object> fields = Maps.newLinkedHashMap();
-        
-        public void putAll(Map<String,?> entries) {
-            fields.putAll(entries);
-        }
-        
-        public void putAll(String keyPrefix, Map<String,?> entries) {
-            for (Map.Entry<String,?> entry : entries.entrySet()) {
-                fields.put(keyPrefix+entry.getKey(), entry.getValue());
-            }
-        }
-        
-        public void put(String key, Object val) {
-            fields.put(key, val);
-        }
-        
-        @Override
-        public String toString() {
-            return fields.toString();
-        }
-    }
-    
-    public static class FileBasedStatusRecorder implements StatusRecorder {
-        private final File outFile;
-    
-        public FileBasedStatusRecorder(File outFile) {
-            this.outFile = outFile;
-        }
-        
-        @Override
-        public void record(Record record) throws IOException {
-            Files.append(record.fields.toString()+"\n", outFile, Charsets.UTF_8);
-        }
-    }
-    
-    public static class SysoutBasedStatusRecorder implements StatusRecorder {
-        public SysoutBasedStatusRecorder() {
-        }
-        
-        @Override
-        public void record(Record record) {
-            System.out.println(record.fields);
-        }
-    }
-    
-    public static class LogBasedStatusRecorder implements StatusRecorder {
-        private final Logger log;
-
-        public LogBasedStatusRecorder(Logger log) {
-            this.log = log;
-        }
-        
-        @Override
-        public void record(Record record) {
-            log.info("{}", record.fields);
-        }
-    }
-    
-    public static class ChainingStatusRecorder implements StatusRecorder {
-        private final StatusRecorder[] recorders;
-
-        public ChainingStatusRecorder(StatusRecorder... recorders) {
-            this.recorders = recorders;
-        }
-        
-        @Override
-        public void record(Record record) throws IOException {
-            for (StatusRecorder recorder : recorders) {
-                recorder.record(record);
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bd44bb8f/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedJBoss7ServerImpl.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedJBoss7ServerImpl.java b/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedJBoss7ServerImpl.java
new file mode 100644
index 0000000..762c17b
--- /dev/null
+++ b/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedJBoss7ServerImpl.java
@@ -0,0 +1,240 @@
+/*
+ * 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.qa.load;
+
+import static java.lang.String.format;
+
+import java.util.concurrent.Callable;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.enricher.Enrichers;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.webapp.jboss.JBoss7ServerImpl;
+import brooklyn.entity.webapp.jboss.JBoss7SshDriver;
+import brooklyn.event.feed.function.FunctionFeed;
+import brooklyn.event.feed.function.FunctionPollConfig;
+import brooklyn.event.feed.http.HttpFeed;
+import brooklyn.event.feed.http.HttpPollConfig;
+import brooklyn.event.feed.http.HttpValueFunctions;
+import brooklyn.location.access.BrooklynAccessUtils;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.guava.Functionals;
+import brooklyn.util.os.Os;
+
+import com.google.common.net.HostAndPort;
+
+/**
+ * For simulating various aspects of the JBoss 7 app-server entity.
+ *  
+ * The use-case for this is that the desired configuration is not always available for testing. 
+ * For example, there may be insufficient resources to run 100s of JBoss app-servers, or one 
+ * may be experimenting with possible configurations such as use of an external monitoring tool 
+ * that is not yet available.
+ * 
+ * It is then possible to simulate aspects of the behaviour, for performance and load testing purposes. 
+ * 
+ * There is configuration for:
+ * <ul>
+ *   <li>{@code simulateEntity}
+ *     <ul>
+ *       <li>if true, no underlying entity will be started. Instead a sleep 100000 job will be run and monitored.
+ *       <li>if false, the underlying entity (i.e. a JBoss app-server) will be started as normal.
+ *     </ul>
+ *   <li>{@code simulateExternalMonitoring}
+ *     <ul>
+ *       <li>if true, disables the default monitoring mechanism. Instead, a function will periodically execute 
+ *           to set the entity's sensors (as though the values had been obtained from the external monitoring tool).
+ *       <li>if false, then:
+ *         <ul>
+ *           <li>If {@code simulateEntity==true} it will execute comparable commands (e.g. execute a command of the same 
+ *               size over ssh or do a comparable number of http GET requests).
+ *           <li>If {@code simulateEntity==false} then normal monitoring will be done.
+ *         </ul>
+ *     </ul>
+ *   <li>{@code skipSshOnStart}
+ *     <ul>
+ *       <li>If true (and if {@code simulateEntity==true}), then no ssh commands will be executed at deploy-time. 
+ *           This is useful for speeding up load testing, to get to the desired number of entities.
+ *           Should not be set to {@code true} if {@code simulateEntity==false}.
+ *       <li>If false, the ssh commands will be executed (based on the value of {@code simulateEntity}.
+ *     </ul>
+ * </ul>
+ */
+public class SimulatedJBoss7ServerImpl extends JBoss7ServerImpl {
+
+    public static final ConfigKey<Boolean> SIMULATE_ENTITY = SimulatedTheeTierApp.SIMULATE_ENTITY;
+    public static final ConfigKey<Boolean> SIMULATE_EXTERNAL_MONITORING = SimulatedTheeTierApp.SIMULATE_EXTERNAL_MONITORING;
+    public static final ConfigKey<Boolean> SKIP_SSH_ON_START = SimulatedTheeTierApp.SKIP_SSH_ON_START;
+    
+    private HttpFeed httpFeed;
+    private FunctionFeed functionFeed;
+    
+    @Override
+    public Class<?> getDriverInterface() {
+        return SimulatedJBoss7SshDriver.class;
+    }
+
+    @Override
+    protected void connectSensors() {
+        boolean simulateEntity = getConfig(SIMULATE_ENTITY);
+        boolean simulateExternalMonitoring = getConfig(SIMULATE_EXTERNAL_MONITORING);
+
+        if (!simulateEntity && !simulateExternalMonitoring) {
+            super.connectSensors();
+            return;
+        }
+        
+        HostAndPort hp = BrooklynAccessUtils.getBrooklynAccessibleAddress(this,
+                getAttribute(MANAGEMENT_HTTP_PORT) + getConfig(PORT_INCREMENT));
+
+        String managementUri = String.format("http://%s:%s/management/subsystem/web/connector/http/read-resource",
+                hp.getHostText(), hp.getPort());
+        setAttribute(MANAGEMENT_URL, managementUri);
+
+        if (simulateExternalMonitoring) {
+            // TODO What would set this normally, if not doing connectServiceUpIsRunning?
+            setAttribute(SERVICE_PROCESS_IS_RUNNING, true);
+        } else {
+            // if simulating entity, then simulate work of periodic HTTP request; TODO but not parsing JSON response
+            String uriToPoll = (simulateEntity) ? "http://localhost:8081" : managementUri;
+            
+            httpFeed = HttpFeed.builder()
+                    .entity(this)
+                    .period(200)
+                    .baseUri(uriToPoll)
+                    .credentials(getConfig(MANAGEMENT_USER), getConfig(MANAGEMENT_PASSWORD))
+                    .poll(new HttpPollConfig<Integer>(MANAGEMENT_STATUS)
+                            .onSuccess(HttpValueFunctions.responseCode()))
+                    .build();
+            
+            // Polls over ssh for whether process is running
+            connectServiceUpIsRunning();
+        }
+        
+        functionFeed = FunctionFeed.builder()
+                .entity(this)
+                .period(5000)
+                .poll(new FunctionPollConfig<Boolean,Boolean>(MANAGEMENT_URL_UP)
+                        .callable(new Callable<Boolean>() {
+                            private int counter = 0;
+                            public Boolean call() {
+                                setAttribute(REQUEST_COUNT, (counter++ % 100));
+                                setAttribute(ERROR_COUNT, (counter++ % 100));
+                                setAttribute(TOTAL_PROCESSING_TIME, (counter++ % 100));
+                                setAttribute(MAX_PROCESSING_TIME, (counter++ % 100));
+                                setAttribute(BYTES_RECEIVED, (long) (counter++ % 100));
+                                setAttribute(BYTES_SENT, (long) (counter++ % 100));
+                                return true;
+                            }}))
+                .build();
+        
+        addEnricher(Enrichers.builder().updatingMap(Attributes.SERVICE_NOT_UP_INDICATORS)
+                .from(MANAGEMENT_URL_UP)
+                .computing(Functionals.ifNotEquals(true).value("Management URL not reachable") )
+                .build());
+    }
+
+    @Override
+    protected void disconnectSensors() {
+        super.disconnectSensors();
+        if (httpFeed != null) httpFeed.stop();
+        if (functionFeed != null) functionFeed.stop();
+    }
+    
+    public static class SimulatedJBoss7SshDriver extends JBoss7SshDriver {
+        public SimulatedJBoss7SshDriver(SimulatedJBoss7ServerImpl entity, SshMachineLocation machine) {
+            super(entity, machine);
+        }
+        
+        @Override
+        public boolean isRunning() {
+            if (entity.getConfig(SKIP_SSH_ON_START)) {
+                return true;
+            } else {
+                return super.isRunning();
+            }
+        }
+
+        @Override
+        public void install() {
+            if (entity.getConfig(SKIP_SSH_ON_START)) {
+                // no-op
+            } else {
+                super.install();
+            }
+        }
+        
+        @Override
+        public void customize() {
+            if (entity.getConfig(SKIP_SSH_ON_START)) {
+                // no-op
+            } else {
+                super.customize();
+            }
+        }
+        
+        @Override
+        public void launch() {
+            if (!entity.getConfig(SIMULATE_ENTITY)) {
+                super.launch();
+                return;
+            }
+            
+            // We wait for evidence of JBoss running because, using
+            // brooklyn.ssh.config.tool.class=brooklyn.util.internal.ssh.cli.SshCliTool,
+            // we saw the ssh session return before the JBoss process was fully running
+            // so the process failed to start.
+            String pidFile = Os.mergePathsUnix(getRunDir(), PID_FILENAME);
+
+            if (entity.getConfig(SKIP_SSH_ON_START)) {
+                // minimal ssh, so that isRunning will subsequently work
+                newScript(MutableMap.of("usePidFile", pidFile), LAUNCHING)
+                        .body.append(
+                                format("nohup sleep 100000 > %s/console 2>&1 < /dev/null &", getRunDir()))
+                        .execute();
+            } else {
+                newScript(MutableMap.of(USE_PID_FILE, false), LAUNCHING)
+                        .body.append(
+                                "export LAUNCH_JBOSS_IN_BACKGROUND=true",
+                                format("export JBOSS_HOME=%s", getExpandedInstallDir()),
+                                format("export JBOSS_PIDFILE=%s/%s", getRunDir(), PID_FILENAME),
+                                format("echo skipping exec of %s/bin/%s.sh ", getExpandedInstallDir(), SERVER_TYPE) +
+                                        format("--server-config %s ", CONFIG_FILE) +
+                                        format("-Djboss.server.base.dir=%s/%s ", getRunDir(), SERVER_TYPE) +
+                                        format("\"-Djboss.server.base.url=file://%s/%s\" ", getRunDir(), SERVER_TYPE) +
+                                        "-Djava.net.preferIPv4Stack=true " +
+                                        "-Djava.net.preferIPv6Addresses=false " +
+                                        format(" >> %s/console 2>&1 </dev/null &", getRunDir()),
+                                format("nohup sleep 100000 > %s/console 2>&1 < /dev/null &", getRunDir()),
+                                format("echo $! > "+pidFile),
+                                format("echo starting > %s/console", getRunDir()),
+                                "for i in {1..10}\n" +
+                                        "do\n" +
+                                        "    grep -i 'starting' "+getRunDir()+"/console && exit\n" +
+                                        "    sleep 1\n" +
+                                        "done\n" +
+                                        "echo \"Couldn't determine if process is running (console output does not contain 'starting'); continuing but may subsequently fail\""
+    
+                            )
+                        .execute();
+            }
+        }
+    }
+}


[05/12] incubator-brooklyn git commit: brooklyn-qa: add org.apache package prefix inside hello-world.war

Posted by al...@apache.org.
brooklyn-qa: add org.apache package prefix inside hello-world.war


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/0cdb2ed9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/0cdb2ed9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/0cdb2ed9

Branch: refs/heads/master
Commit: 0cdb2ed9d511c655f09daa0f72213555bbbe0920
Parents: bd44bb8
Author: Ciprian Ciubotariu <ch...@gmx.net>
Authored: Wed Jul 15 18:46:25 2015 +0300
Committer: Ciprian Ciubotariu <ch...@gmx.net>
Committed: Wed Jul 15 18:47:00 2015 +0300

----------------------------------------------------------------------
 usage/qa/src/main/resources/hello-world.war | Bin 14693 -> 15066 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0cdb2ed9/usage/qa/src/main/resources/hello-world.war
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/resources/hello-world.war b/usage/qa/src/main/resources/hello-world.war
index c692ca8..0e17460 100644
Binary files a/usage/qa/src/main/resources/hello-world.war and b/usage/qa/src/main/resources/hello-world.war differ