You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by dr...@apache.org on 2017/05/12 16:13:18 UTC

[1/2] brooklyn-library git commit: Temporarily restore simple-web-cluster.

Repository: brooklyn-library
Updated Branches:
  refs/heads/master 8c3ad2786 -> 3b96d1d1c


Temporarily restore simple-web-cluster.

A file in it is still in use at https://github.com/apache/brooklyn-library/blob/master/qa/src/test/resources/java-web-app-and-db-with-policy.bom#L54

Will need to tidy that up so the file is no longer used and then
this example can be deleted again.


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

Branch: refs/heads/master
Commit: 24296fe813e6c707269d33652f925b81097eb8bf
Parents: 8c3ad27
Author: Geoff Macartney <ge...@cloudsoftcorp.com>
Authored: Fri May 12 15:29:07 2017 +0100
Committer: Geoff Macartney <ge...@cloudsoftcorp.com>
Committed: Fri May 12 15:29:07 2017 +0100

----------------------------------------------------------------------
 examples/pom.xml                                |   1 +
 examples/simple-web-cluster/.gitignore          |   2 +
 examples/simple-web-cluster/README.txt          |  59 ++++++
 examples/simple-web-cluster/pom.xml             | 164 +++++++++++++++
 .../resources/jmeter-test-plan.jmx              | 143 +++++++++++++
 .../src/main/assembly/assembly.xml              |  74 +++++++
 .../src/main/assembly/files/README.txt          |  49 +++++
 .../src/main/assembly/scripts/start.sh          |  43 ++++
 .../brooklyn/demo/NodeJsTodoApplication.java    |  60 ++++++
 .../brooklyn/demo/SingleWebServerExample.java   |  66 ++++++
 .../demo/WebClusterDatabaseExample.java         | 122 +++++++++++
 .../demo/WebClusterDatabaseExampleApp.java      | 174 ++++++++++++++++
 .../apache/brooklyn/demo/WebClusterExample.java |  95 +++++++++
 .../src/main/resources/catalog.bom              |  31 +++
 .../src/main/resources/logback-custom.xml       |  43 ++++
 .../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 +++++
 .../main/resources/visitors-creation-script.sql |  41 ++++
 ...lusterDatabaseExampleAppIntegrationTest.java | 204 +++++++++++++++++++
 20 files changed, 1470 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/examples/pom.xml
----------------------------------------------------------------------
diff --git a/examples/pom.xml b/examples/pom.xml
index ac03e8e..697a3f2 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -37,6 +37,7 @@
     </parent>
 
     <modules>
+        <module>simple-web-cluster</module>
         <module>webapps</module>
     </modules>
 </project>

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/examples/simple-web-cluster/.gitignore
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/.gitignore b/examples/simple-web-cluster/.gitignore
new file mode 100644
index 0000000..9a3d6d6
--- /dev/null
+++ b/examples/simple-web-cluster/.gitignore
@@ -0,0 +1,2 @@
+brooklyn-example-simple-web-cluster/
+brooklyn-example-simple-web-cluster.tar.gz

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/examples/simple-web-cluster/README.txt
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/README.txt b/examples/simple-web-cluster/README.txt
new file mode 100644
index 0000000..598a4ce
--- /dev/null
+++ b/examples/simple-web-cluster/README.txt
@@ -0,0 +1,59 @@
+Instructions for Running Examples
+=================================
+
+The commands below assume that the `brooklyn` script is on your $PATH, this project has been built,
+and you are in this directory.  Adjust to taste for other configurations.
+
+  export BROOKLYN_CLASSPATH=$(pwd)/target/classes
+  
+  # Three-tier: auto-scaling app-server cluster fronted by nginx, MySql backend wired up, on localhost
+  brooklyn launch --app org.apache.brooklyn.demo.WebClusterDatabaseExample --location localhost
+
+The above requires passwordless `ssh localhost` and requires `gcc` to build `nginx`.
+You could instead target your favourite cloud, where this has been tried and tested:
+
+  # Same three-tier, but in Amazon California, prompting for credentials
+  # (the location arg can be changed for any example, of course, and other clouds are available)
+  export JCLOUDS_AWS_EC2_IDENTITY=AKA50M30N3S1DFR0MAW55
+  export JCLOUDS_AWS_EC2_CREDENTIAL=aT0Ps3cr3tC0D3wh1chAW5w1llG1V3y0uTOus333
+  brooklyn launch --app brooklyn.demo.WebClusterDatabaseExample --location aws-ec2:us-west-1
+
+
+Other examples:
+
+  # A very simple app: a single web-server
+  brooklyn launch --app org.apache.brooklyn.demo.SingleWebServerExample --location localhost
+
+  # A simple app: just load-balancer and appservers
+  brooklyn launch --app org.apache.brooklyn.demo.WebClusterExample --location localhost
+
+  # Three-tier example
+  brooklyn launch --app org.apache.brooklyn.demo.WebClusterDatabaseExample --location localhost
+
+
+Redistributable embedded example:
+
+  # To build a redistributable tar.gz with a start.sh script
+  # which invokes the `main` method in the example class to start
+  # (the redistributable will be at:  target/brooklyn-*-bin.tar.gz )
+  mvn clean assembly:assembly
+
+For more information please visit https://brooklyn.incubator.apache.org/.
+
+----
+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.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/examples/simple-web-cluster/pom.xml
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/pom.xml b/examples/simple-web-cluster/pom.xml
new file mode 100644
index 0000000..ef5cd67
--- /dev/null
+++ b/examples/simple-web-cluster/pom.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+    <artifactId>brooklyn-example-simple-web-cluster</artifactId>
+    <name>Brooklyn Simple Web Cluster Example</name>
+
+    <!-- this is only needed for "mvn deploy"; the group and version can be extracted
+         and parent block removed to run this standalone -->
+    <parent>
+        <groupId>org.apache.brooklyn.example</groupId>
+        <artifactId>brooklyn-examples-parent</artifactId>
+        <version>0.12.0-SNAPSHOT</version>   <!-- BROOKLYN_VERSION -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-launcher</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-software-webapp</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-software-database</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-software-nosql</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-logback-xml</artifactId>
+            <version>${project.version}</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-core</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-test-support</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <!-- copy the WAR so it is available on the classpath for programmatic deployment -->
+                <executions>
+                    <execution>
+                        <id>copy</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <!-- these can fail in eclipse trying to copy _from_ target/classes.
+                                         see http://jira.codehaus.org/browse/MDEP-259 -->
+                                <artifactItem>
+                                    <groupId>org.apache.brooklyn.example</groupId>
+                                    <artifactId>brooklyn-example-hello-world-webapp</artifactId>
+                                    <version>${project.version}</version>
+                                    <type>war</type>
+                                    <overWrite>true</overWrite>
+                                    <outputDirectory>target/classes</outputDirectory>
+                                    <destFileName>hello-world-webapp.war</destFileName>
+                                </artifactItem>
+                                <artifactItem>
+                                    <groupId>org.apache.brooklyn.example</groupId>
+                                    <artifactId>brooklyn-example-hello-world-sql-webapp</artifactId>
+                                    <version>${project.version}</version>
+                                    <type>war</type>
+                                    <overWrite>true</overWrite>
+                                    <outputDirectory>target/classes</outputDirectory>
+                                    <destFileName>hello-world-sql-webapp.war</destFileName>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-clean-plugin</artifactId>
+                <configuration>
+                    <filesets>
+                        <fileset>
+                            <directory>${project.basedir}</directory>
+							<includes>
+                                <include>${project.artifactId}/</include>
+                                <include>brooklyn*.log</include>
+                                <include>brooklyn*.log.*</include>
+                                <include>stacktrace.log</include>
+                            </includes>
+                        </fileset>
+                    </filesets>
+                </configuration>
+            </plugin>            
+            <plugin>
+                <!-- optional, with this block, `mvn assembly:assembly` will build a redistributable tgz -->
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <descriptors>
+                        <descriptor>src/main/assembly/assembly.xml</descriptor>
+                    </descriptors>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+        
+</project>

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/examples/simple-web-cluster/resources/jmeter-test-plan.jmx
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/resources/jmeter-test-plan.jmx b/examples/simple-web-cluster/resources/jmeter-test-plan.jmx
new file mode 100644
index 0000000..0eb1741
--- /dev/null
+++ b/examples/simple-web-cluster/resources/jmeter-test-plan.jmx
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<jmeterTestPlan version="1.2" properties="2.1">
+  <hashTree>
+    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Brooklyn local webapp test - load for localhost:8000" enabled="true">
+      <stringProp name="TestPlan.comments"></stringProp>
+      <boolProp name="TestPlan.functional_mode">false</boolProp>
+      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
+      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
+        <collectionProp name="Arguments.arguments"/>
+      </elementProp>
+      <stringProp name="TestPlan.user_define_classpath"></stringProp>
+    </TestPlan>
+    <hashTree>
+      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Test Group - hitting site with 8 threads" enabled="true">
+        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
+        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
+          <boolProp name="LoopController.continue_forever">false</boolProp>
+          <intProp name="LoopController.loops">-1</intProp>
+        </elementProp>
+        <stringProp name="ThreadGroup.num_threads">8</stringProp>
+        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
+        <longProp name="ThreadGroup.start_time">1326116677000</longProp>
+        <longProp name="ThreadGroup.end_time">1326116677000</longProp>
+        <boolProp name="ThreadGroup.scheduler">false</boolProp>
+        <stringProp name="ThreadGroup.duration"></stringProp>
+        <stringProp name="ThreadGroup.delay"></stringProp>
+      </ThreadGroup>
+      <hashTree>
+        <ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="Timer - frequency 50 reqs/sec per thread (20 ms delay)" enabled="true">
+          <stringProp name="ConstantTimer.delay">20</stringProp>
+        </ConstantTimer>
+        <hashTree/>
+        <HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="HTTP Request - localhost:8000" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain">localhost</stringProp>
+          <stringProp name="HTTPSampler.port">8000</stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path"></stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <boolProp name="HTTPSampler.monitor">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+        </HTTPSampler>
+        <hashTree/>
+        <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true">
+          <collectionProp name="CookieManager.cookies"/>
+          <boolProp name="CookieManager.clearEachIteration">false</boolProp>
+        </CookieManager>
+        <hashTree/>
+      </hashTree>
+      <ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="true">
+        <boolProp name="ResultCollector.error_logging">false</boolProp>
+        <objProp>
+          <name>saveConfig</name>
+          <value class="SampleSaveConfiguration">
+            <time>true</time>
+            <latency>true</latency>
+            <timestamp>true</timestamp>
+            <success>true</success>
+            <label>true</label>
+            <code>true</code>
+            <message>true</message>
+            <threadName>true</threadName>
+            <dataType>true</dataType>
+            <encoding>false</encoding>
+            <assertions>true</assertions>
+            <subresults>true</subresults>
+            <responseData>false</responseData>
+            <samplerData>false</samplerData>
+            <xml>true</xml>
+            <fieldNames>false</fieldNames>
+            <responseHeaders>false</responseHeaders>
+            <requestHeaders>false</requestHeaders>
+            <responseDataOnError>false</responseDataOnError>
+            <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
+            <assertionsResultsToSave>0</assertionsResultsToSave>
+            <bytes>true</bytes>
+          </value>
+        </objProp>
+        <stringProp name="filename"></stringProp>
+      </ResultCollector>
+      <hashTree/>
+      <ResultCollector guiclass="SplineVisualizer" testclass="ResultCollector" testname="Spline Visualizer" enabled="true">
+        <boolProp name="ResultCollector.error_logging">false</boolProp>
+        <objProp>
+          <name>saveConfig</name>
+          <value class="SampleSaveConfiguration">
+            <time>true</time>
+            <latency>true</latency>
+            <timestamp>true</timestamp>
+            <success>true</success>
+            <label>true</label>
+            <code>true</code>
+            <message>true</message>
+            <threadName>true</threadName>
+            <dataType>true</dataType>
+            <encoding>false</encoding>
+            <assertions>true</assertions>
+            <subresults>true</subresults>
+            <responseData>false</responseData>
+            <samplerData>false</samplerData>
+            <xml>true</xml>
+            <fieldNames>false</fieldNames>
+            <responseHeaders>false</responseHeaders>
+            <requestHeaders>false</requestHeaders>
+            <responseDataOnError>false</responseDataOnError>
+            <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
+            <assertionsResultsToSave>0</assertionsResultsToSave>
+            <bytes>true</bytes>
+          </value>
+        </objProp>
+        <stringProp name="filename"></stringProp>
+      </ResultCollector>
+      <hashTree/>
+    </hashTree>
+  </hashTree>
+</jmeterTestPlan>

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/examples/simple-web-cluster/src/main/assembly/assembly.xml
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/assembly/assembly.xml b/examples/simple-web-cluster/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..cbe7a08
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/assembly/assembly.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<assembly>
+    <id>bin</id>
+    <!-- Generates an archive containing the needed files; 
+         can add e.g. zip to the following
+         (but executable bit is not preserved) -->
+    <formats>
+        <format>tar.gz</format>
+    </formats>
+
+    <!-- Adds dependencies to zip package under lib directory -->
+    <dependencySets>
+        <dependencySet>
+            <!--
+               Project artifact is not copied under library directory since
+               it is added to the root directory of the zip package.
+           -->
+            <useProjectArtifact>false</useProjectArtifact>
+            <outputDirectory>lib</outputDirectory>
+            <unpack>false</unpack>
+        </dependencySet>
+    </dependencySets>
+
+    <fileSets>
+        <!--
+           Adds startup scripts to the root directory of zip package. The startup
+           scripts are located to src/main/scripts directory as stated by Maven
+           conventions.
+       -->
+        <fileSet>
+            <directory>src/main/assembly/scripts</directory>
+            <outputDirectory></outputDirectory>
+            <fileMode>0755</fileMode>
+            <includes>
+                <include>*</include>
+            </includes>
+        </fileSet>
+	<!-- add additional files (but not marked executable -->
+        <fileSet>
+            <directory>src/main/assembly/files</directory>
+            <outputDirectory></outputDirectory>
+            <includes>
+                <include>*</include>
+            </includes>
+        </fileSet>
+        <!-- adds jar package to the root directory of zip package -->
+        <fileSet>
+            <directory>target</directory>
+            <outputDirectory></outputDirectory>
+            <includes>
+                <include>*.jar</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+</assembly>
+

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/examples/simple-web-cluster/src/main/assembly/files/README.txt
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/assembly/files/README.txt b/examples/simple-web-cluster/src/main/assembly/files/README.txt
new file mode 100644
index 0000000..cf49ea5
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/assembly/files/README.txt
@@ -0,0 +1,49 @@
+Brooklyn Example
+================
+
+To use, configure your cloud credentials then run  ./start.sh  in this directory.
+You can then access the management context in your browser, typically on  localhost:8081.
+
+
+### Cloud Credentials
+
+To run, you'll need to specify credentials for your preferred cloud.  This can be done 
+in `~/.brooklyn/brooklyn.properties`:
+
+    brooklyn.jclouds.aws-ec2.identity=AKXXXXXXXXXXXXXXXXXX
+    brooklyn.jclouds.aws-ec2.credential=secret01xxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+Alternatively these can be set as shell environment parameters or JVM system properties.
+
+Many other clouds are supported also, as well as pre-existing machines ("bring your own nodes"),
+custom endpoints for private clouds, and specifying custom keys and passphrases.
+For more information see:
+
+    https://github.com/brooklyncentral/brooklyn/blob/master/docs/use/guide/defining-applications/common-usage.md#off-the-shelf-locations
+
+
+### Run
+
+Usage:
+
+    ./start.sh [--port 8081+] location
+
+Where location might be `localhost` (the defaul), or `aws-ec2:us-east-1`, `openstack:endpoint`, etc.
+
+----
+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.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/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
new file mode 100755
index 0000000..ec4f110
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/assembly/scripts/start.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# 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.
+#
+
+if [ -z "$BROOKLYN_APP_CLASS" ] ; then 
+    BROOKLYN_APP_CLASS=org.apache.brooklyn.demo.WebClusterDatabaseExample
+fi
+
+if [ -z "$JAVA" ] ; then 
+    JAVA=`which java`
+fi
+if [ ! -z "$JAVA_HOME" ] ; then 
+    JAVA=$JAVA_HOME/bin/java
+else
+    JAVA=`which java`
+fi
+if [ ! -x "$JAVA" ] ; then
+  echo Cannot find java. Set JAVA_HOME or add java to path.
+  exit 1
+fi
+
+if [[ ! `ls *.jar 2> /dev/null` || ! `ls lib/*.jar 2> /dev/null` ]] ; then
+  echo Command must be run from the directory where it is installed.
+  exit 1
+fi
+
+$JAVA -Xms256m -Xmx1024m -XX:MaxPermSize=1024m -classpath "*:lib/*" $BROOKLYN_APP_CLASS "$@"

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/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..086f390
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/NodeJsTodoApplication.java
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.brooklyn.core.sensor.DependentConfiguration.attributeWhenReady;
+
+import org.apache.brooklyn.api.catalog.Catalog;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.core.entity.AbstractApplication;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.StartableApplication;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.sensor.DependentConfiguration;
+import org.apache.brooklyn.entity.nosql.redis.RedisStore;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.entity.webapp.nodejs.NodeJsWebAppService;
+
+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/brooklyn-library/blob/24296fe8/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..cec7a7e
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/SingleWebServerExample.java
@@ -0,0 +1,66 @@
+/*
+ * 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 org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.core.entity.AbstractApplication;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.StartableApplication;
+import org.apache.brooklyn.core.location.PortRanges;
+import org.apache.brooklyn.entity.webapp.JavaWebAppService;
+import org.apache.brooklyn.entity.webapp.jboss.JBoss7Server;
+import org.apache.brooklyn.launcher.BrooklynLauncher;
+import org.apache.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/brooklyn-library/blob/24296fe8/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..601537a
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExample.java
@@ -0,0 +1,122 @@
+/*
+ * 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 org.apache.brooklyn.core.sensor.DependentConfiguration.attributeWhenReady;
+import static org.apache.brooklyn.core.sensor.DependentConfiguration.formatString;
+import static org.apache.brooklyn.entity.java.JavaEntityMethods.javaSysProp;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.entity.database.mysql.MySqlNode;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.entity.AbstractApplication;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.StartableApplication;
+import org.apache.brooklyn.core.location.PortRanges;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.enricher.stock.Enrichers;
+import org.apache.brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
+import org.apache.brooklyn.entity.webapp.DynamicWebAppCluster;
+import org.apache.brooklyn.entity.webapp.JavaWebAppService;
+import org.apache.brooklyn.entity.webapp.WebAppService;
+import org.apache.brooklyn.entity.webapp.WebAppServiceConstants;
+import org.apache.brooklyn.launcher.BrooklynLauncher;
+import org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy;
+import org.apache.brooklyn.policy.enricher.HttpLatencyDetector;
+import org.apache.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.enrichers().add(HttpLatencyDetector.builder().
+                url(ControlledDynamicWebAppCluster.ROOT_URL).
+                rollup(10, TimeUnit.SECONDS).
+                build());
+
+        // simple scaling policy
+        web.getCluster().policies().add(AutoScalerPolicy.builder().
+                metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW_PER_NODE).
+                metricRange(10, 100).
+                sizeRange(1, 5).
+                build());
+
+        // expose some KPI's
+        enrichers().add(Enrichers.builder()
+                .propagating(WebAppServiceConstants.ROOT_URL,
+                        DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW,
+                        HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW)
+                .from(web)
+                .build());
+
+        enrichers().add(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/brooklyn-library/blob/24296fe8/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..5c58fee
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleApp.java
@@ -0,0 +1,174 @@
+/*
+ * 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 org.apache.brooklyn.core.sensor.DependentConfiguration.attributeWhenReady;
+import static org.apache.brooklyn.core.sensor.DependentConfiguration.formatString;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.api.catalog.Catalog;
+import org.apache.brooklyn.api.catalog.CatalogConfig;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.entity.AbstractApplication;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.StartableApplication;
+import org.apache.brooklyn.core.location.PortRanges;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.enricher.stock.Enrichers;
+import org.apache.brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
+import org.apache.brooklyn.entity.webapp.DynamicWebAppCluster;
+import org.apache.brooklyn.entity.webapp.JavaWebAppService;
+import org.apache.brooklyn.entity.webapp.WebAppService;
+import org.apache.brooklyn.entity.webapp.WebAppServiceConstants;
+import org.apache.brooklyn.entity.database.mysql.MySqlNode;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.java.JavaEntityMethods;
+import org.apache.brooklyn.launcher.BrooklynLauncher;
+import org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy;
+import org.apache.brooklyn.policy.enricher.HttpLatencyDetector;
+import org.apache.brooklyn.util.CommandLineUtil;
+import org.apache.brooklyn.util.core.BrooklynMavenArtifacts;
+import org.apache.brooklyn.util.core.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.enrichers().add(HttpLatencyDetector.builder()
+                .url(ROOT_URL)
+                .rollup(10, TimeUnit.SECONDS)
+                .build());
+        
+        web.getCluster().policies().add(AutoScalerPolicy.builder()
+                .metric(DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW_PER_NODE)
+                .metricRange(10, 100)
+                .sizeRange(2, 5)
+                .build());
+
+        enrichers().add(Enrichers.builder()
+                .propagating(WebAppServiceConstants.ROOT_URL,
+                        DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW,
+                        HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW)
+                .from(web)
+                .build());
+
+        enrichers().add(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/brooklyn-library/blob/24296fe8/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..e5a1ae2
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterExample.java
@@ -0,0 +1,95 @@
+/*
+ * 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 org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.core.entity.AbstractApplication;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.entity.proxy.nginx.NginxController;
+import org.apache.brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
+import org.apache.brooklyn.entity.webapp.DynamicWebAppCluster;
+import org.apache.brooklyn.entity.webapp.jboss.JBoss7Server;
+import org.apache.brooklyn.launcher.BrooklynLauncher;
+import org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy;
+import org.apache.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().policies().add(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/brooklyn-library/blob/24296fe8/examples/simple-web-cluster/src/main/resources/catalog.bom
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/resources/catalog.bom b/examples/simple-web-cluster/src/main/resources/catalog.bom
new file mode 100644
index 0000000..4d5cd8c
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/resources/catalog.bom
@@ -0,0 +1,31 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+brooklyn.catalog:
+    version: "0.12.0-SNAPSHOT" # BROOKLYN_VERSION
+    itemType: template
+    items:
+    - id: org.apache.brooklyn.demo.NodeJsTodoApplication
+      item:
+        services:
+        - type: org.apache.brooklyn.demo.NodeJsTodoApplication
+          name: NodeJS Todo
+    - id: org.apache.brooklyn.demo.WebClusterDatabaseExampleApp
+      item:
+        services:
+        - type: org.apache.brooklyn.demo.WebClusterDatabaseExampleApp
+          name: Elastic Java Web + DB

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/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
new file mode 100644
index 0000000..02a8a82
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/resources/logback-custom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<included>
+
+    <!--
+     
+        this file demonstrates how to customise logging, base on the placeholder 
+        logging-custom.xml included in brooklyn-logging-includes.
+
+    -->        
+  
+    <!-- include this category -->
+    <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" />
+
+  <!--
+  
+       more customisation is possible by overriding the default-included
+       configuration files, such as logback-main.xml (entirely replacing configuration),
+       or e.g. brooklyn/logback-appender-file.xml (replacing the FILE logger config).
+       
+  -->
+
+</included>

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/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/brooklyn-library/blob/24296fe8/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..958d29d
--- /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: org.apache.brooklyn.entity.nosql.riak.RiakCluster
+  initialSize: 2
+  id: mycluster
+  brooklyn.config:
+    provisioning.properties:
+      osFamily: centos
+      minCores: 4
+      minRam: 2048
+- type: org.apache.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/brooklyn-library/blob/24296fe8/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..6aab1db
--- /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: org.apache.brooklyn.entity.nosql.redis.RedisStore
+  id: redis
+  name: "Redis"
+- type: org.apache.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/brooklyn-library/blob/24296fe8/examples/simple-web-cluster/src/main/resources/visitors-creation-script.sql
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/resources/visitors-creation-script.sql b/examples/simple-web-cluster/src/main/resources/visitors-creation-script.sql
new file mode 100644
index 0000000..2422f8f
--- /dev/null
+++ b/examples/simple-web-cluster/src/main/resources/visitors-creation-script.sql
@@ -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.
+--
+create database visitors;
+use visitors;
+
+# not necessary to create user if we grant (and not supported in some dialects)
+# create user 'brooklyn' identified by 'br00k11n';
+
+grant usage on *.* to 'brooklyn'@'%' identified by 'br00k11n';
+
+# ''@localhost is sometimes set up, overriding brooklyn@'%', so do a second explicit grant
+grant usage on *.* to 'brooklyn'@'localhost' identified by 'br00k11n';
+
+grant all privileges on visitors.* to 'brooklyn'@'%';
+
+flush privileges;
+
+CREATE TABLE MESSAGES (
+        id BIGINT NOT NULL AUTO_INCREMENT,
+        NAME VARCHAR(30) NOT NULL,
+        MESSAGE VARCHAR(400) NOT NULL,
+        PRIMARY KEY (ID)
+    );
+
+INSERT INTO MESSAGES values (default, 'Isaac Asimov', 'I grew up in Brooklyn' );

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/24296fe8/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..2dfbb81
--- /dev/null
+++ b/examples/simple-web-cluster/src/test/java/org/apache/brooklyn/demo/RebindWebClusterDatabaseExampleAppIntegrationTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.sensor.Enricher;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityAsserts;
+import org.apache.brooklyn.core.entity.StartableApplication;
+import org.apache.brooklyn.core.mgmt.rebind.RebindOptions;
+import org.apache.brooklyn.core.mgmt.rebind.RebindTestFixture;
+import org.apache.brooklyn.enricher.stock.Propagator;
+import org.apache.brooklyn.entity.proxy.nginx.NginxController;
+import org.apache.brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
+import org.apache.brooklyn.entity.webapp.DynamicWebAppCluster;
+import org.apache.brooklyn.entity.webapp.tomcat.Tomcat8Server;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.test.HttpTestUtils;
+import org.apache.brooklyn.test.WebAppMonitor;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.time.Duration;
+
+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 org.apache.brooklyn.entity.database.mysql.MySqlNode;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.java.JavaEntityMethods;
+import org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy;
+import org.apache.brooklyn.policy.enricher.HttpLatencyDetector;
+
+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));
+        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.policies(), Predicates.instanceOf(AutoScalerPolicy.class));
+        
+        autoScalerPolicy.config().set(AutoScalerPolicy.MIN_POOL_SIZE, 3);
+        EntityAsserts.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.enrichers(), Predicates.instanceOf(HttpLatencyDetector.class));
+        Iterable<Enricher> propagatorEnrichers = Iterables.filter(web.enrichers(), 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.
+        EntityAsserts.assertAttributeEqualsEventually(app, WebClusterDatabaseExampleApp.APPSERVERS_COUNT, 3);
+        EntityAsserts.assertAttributeChangesEventually(web, DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW);
+        EntityAsserts.assertAttributeChangesEventually(app, DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW);
+        EntityAsserts.assertAttributeChangesEventually(web, HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_MOST_RECENT);
+        EntityAsserts.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);
+        EntityAsserts.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();
+    }
+}


[2/2] brooklyn-library git commit: This closes #109

Posted by dr...@apache.org.
This closes #109


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

Branch: refs/heads/master
Commit: 3b96d1d1cab99bf9e858a85c14d75a08667e5838
Parents: 8c3ad27 24296fe
Author: Duncan Godwin <dr...@googlemail.com>
Authored: Fri May 12 17:13:03 2017 +0100
Committer: Duncan Godwin <dr...@googlemail.com>
Committed: Fri May 12 17:13:03 2017 +0100

----------------------------------------------------------------------
 examples/pom.xml                                |   1 +
 examples/simple-web-cluster/.gitignore          |   2 +
 examples/simple-web-cluster/README.txt          |  59 ++++++
 examples/simple-web-cluster/pom.xml             | 164 +++++++++++++++
 .../resources/jmeter-test-plan.jmx              | 143 +++++++++++++
 .../src/main/assembly/assembly.xml              |  74 +++++++
 .../src/main/assembly/files/README.txt          |  49 +++++
 .../src/main/assembly/scripts/start.sh          |  43 ++++
 .../brooklyn/demo/NodeJsTodoApplication.java    |  60 ++++++
 .../brooklyn/demo/SingleWebServerExample.java   |  66 ++++++
 .../demo/WebClusterDatabaseExample.java         | 122 +++++++++++
 .../demo/WebClusterDatabaseExampleApp.java      | 174 ++++++++++++++++
 .../apache/brooklyn/demo/WebClusterExample.java |  95 +++++++++
 .../src/main/resources/catalog.bom              |  31 +++
 .../src/main/resources/logback-custom.xml       |  43 ++++
 .../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 +++++
 .../main/resources/visitors-creation-script.sql |  41 ++++
 ...lusterDatabaseExampleAppIntegrationTest.java | 204 +++++++++++++++++++
 20 files changed, 1470 insertions(+)
----------------------------------------------------------------------