This closes #1169
diff --git a/LICENSE b/LICENSE
index ef288dc..3d8f4e7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -330,11 +330,12 @@
       Arpad Borsos (2012)
     Used under the BSD 2-Clause license.
 
-This project includes the software: swagger
-  Used under the following license: <no license info>
-
-This project includes the software: swagger-ui
-  Used under the following license: <no license info>
+This project includes the software: Swagger UI
+  Available at: https://github.com/swagger-api/swagger-ui
+  Inclusive of: swagger*.{js,css,html}
+  Version used: 2.1.4
+  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+  Copyright (c) SmartBear Software (2011-2015)
 
 This project includes the software: typeahead.js
   Available at: https://github.com/twitter/typeahead.js
diff --git a/brooklyn-dist/.gitignore b/brooklyn-dist/.gitignore
index ed439f2..2ef22e4 100644
--- a/brooklyn-dist/.gitignore
+++ b/brooklyn-dist/.gitignore
@@ -28,5 +28,6 @@
 brooklyn*.log.*
 
 *brooklyn-persisted-state/
+*.vagrant/
 
 ignored
diff --git a/brooklyn-dist/LICENSE b/brooklyn-dist/LICENSE
index ca60394..3d8f4e7 100644
--- a/brooklyn-dist/LICENSE
+++ b/brooklyn-dist/LICENSE
@@ -282,6 +282,13 @@
   Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
   Copyright (c) Vitaly Puzrin (2011-2015)
 
+This project includes the software: marked.js
+  Available at: https://github.com/chjj/marked
+  Developed by: Christopher Jeffrey (https://github.com/chjj)
+  Version used: 0.3.1
+  Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
+  Copyright (c) Christopher Jeffrey (2011-2014)
+
 This project includes the software: moment.js
   Available at: http://momentjs.com
   Developed by: Tim Wood (http://momentjs.com)
@@ -323,17 +330,10 @@
       Arpad Borsos (2012)
     Used under the BSD 2-Clause license.
 
-This project includes the software: Swagger JS
-  Available at: https://github.com/wordnik/swagger-js
-  Inclusive of: swagger.js
-  Version used: 1.0.1
-  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
-  Copyright (c) SmartBear Software (2011-2015)
-
 This project includes the software: Swagger UI
-  Available at: https://github.com/wordnik/swagger-ui
-  Inclusive of: swagger-ui.js
-  Version used: 1.0.1
+  Available at: https://github.com/swagger-api/swagger-ui
+  Inclusive of: swagger*.{js,css,html}
+  Version used: 2.1.4
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
   Copyright (c) SmartBear Software (2011-2015)
 
diff --git a/brooklyn-dist/dist/licensing/make-all-licenses.sh b/brooklyn-dist/dist/licensing/make-all-licenses.sh
index 3b62f96..b08d17f 100755
--- a/brooklyn-dist/dist/licensing/make-all-licenses.sh
+++ b/brooklyn-dist/dist/licensing/make-all-licenses.sh
@@ -29,26 +29,14 @@
 unset BROOKLYN_LICENSE_EXTRAS_FILES
 unset BROOKLYN_LICENSE_MODE
 
-# individual projects
-for x in `cat projects-with-custom-licenses` ; do
-  export BROOKLYN_LICENSE_MODE=`basename $x`
-  echo MAKING LICENSES FOR: ${BROOKLYN_LICENSE_MODE}
-  export BROOKLYN_LICENSE_SPECIALS=-DonlyExtras=true
-  export BROOKLYN_LICENSE_EXTRAS_FILES=$x/src/main/license/source-inclusions.yaml
-  cp licenses/`basename $x`/* licenses/source
-  ./make-one-license.sh > LICENSE.autogenerated || ( echo FAILED. See details in tmp_stdout/err. && false )
-  cp LICENSE.autogenerated ../$x/src/main/license/files/LICENSE
-  unset BROOKLYN_LICENSE_SPECIALS
-  unset BROOKLYN_LICENSE_EXTRAS_FILES
-  unset BROOKLYN_LICENSE_MODE
-done
-
-# source build, at root
+# source build, at root and each sub-project
 export BROOKLYN_LICENSE_MODE=source
 echo MAKING LICENSES FOR: ${BROOKLYN_LICENSE_MODE}
 export BROOKLYN_LICENSE_SPECIALS=-DonlyExtras=true 
 ./make-one-license.sh > LICENSE.autogenerated
 cp LICENSE.autogenerated ../../../LICENSE
+# overwrite any existing licenses at root
+for x in ../../../brooklyn-*/LICENSE ; do cp LICENSE.autogenerated $x ; done
 unset BROOKLYN_LICENSE_SPECIALS
 unset BROOKLYN_LICENSE_MODE
 
@@ -59,3 +47,19 @@
 cp LICENSE.autogenerated ../src/main/license/files/LICENSE
 unset BROOKLYN_LICENSE_MODE
 
+# individual projects
+for x in `cat projects-with-custom-licenses` ; do
+  export BROOKLYN_LICENSE_MODE=`basename $x`
+  echo MAKING LICENSES FOR: ${BROOKLYN_LICENSE_MODE}
+  export BROOKLYN_LICENSE_SPECIALS=-DonlyExtras=true
+  export BROOKLYN_LICENSE_EXTRAS_FILES=$x/src/main/license/source-inclusions.yaml
+  cp licenses/`basename $x`/* licenses/source
+  ./make-one-license.sh > LICENSE.autogenerated || ( echo FAILED. See details in tmp_stdout/err. && false )
+  cp LICENSE.autogenerated ../$x/src/main/license/files/LICENSE
+  # also copy to root of that project *if* there is already a LICENSE file there
+  [ -f ../$x/LICENSE ] && cp LICENSE.autogenerated ../$x/LICENSE || true
+  unset BROOKLYN_LICENSE_SPECIALS
+  unset BROOKLYN_LICENSE_EXTRAS_FILES
+  unset BROOKLYN_LICENSE_MODE
+done
+
diff --git a/brooklyn-dist/dist/licensing/overrides.yaml b/brooklyn-dist/dist/licensing/overrides.yaml
index a2f6107..1285df4 100644
--- a/brooklyn-dist/dist/licensing/overrides.yaml
+++ b/brooklyn-dist/dist/licensing/overrides.yaml
@@ -99,7 +99,8 @@
 
 # info on non-maven projects
 
-- id: jquery-core
+# used in UI
+- id: jquery-core:1.7.2
   url: http://jquery.com/
   description: JS library for manipulating HTML and eventing
   name: jQuery JavaScript Library
@@ -146,6 +147,7 @@
   - "  Available at http://sizzlejs.com"
   - "  Used under the MIT license"
 
+# not used anymore? swagger-ui includes what it needs, it seems.
 - id: swagger:2.1.6
   name: Swagger JS
   files: swagger-client.js
@@ -154,10 +156,10 @@
   license: ASL2
   notice: Copyright (c) SmartBear Software (2011-2015)
 
-- id: swagger-ui:2.1.3
-  files: swagger-ui.js
+- id: swagger-ui:2.1.4
+  files: swagger*.{js,css,html}
   name: Swagger UI
-  version: 2.1.3
+  version: 2.1.4
   url: https://github.com/swagger-api/swagger-ui
   license: ASL2
   notice: Copyright (c) SmartBear Software (2011-2015)
diff --git a/brooklyn-dist/dist/src/main/license/files/LICENSE b/brooklyn-dist/dist/src/main/license/files/LICENSE
index 39db9db..dd42819 100644
--- a/brooklyn-dist/dist/src/main/license/files/LICENSE
+++ b/brooklyn-dist/dist/src/main/license/files/LICENSE
@@ -413,7 +413,7 @@
 
 This project includes the software: io.cloudsoft.windows
   Available at: http://github.com/cloudsoft/winrm4j
-  Version used: 0.1.0
+  Version used: 0.2.0
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
 This project includes the software: io.swagger
@@ -576,37 +576,37 @@
 This project includes the software: org.apache.jclouds
   Available at: http://jclouds.apache.org/
   Developed by: The Apache Software Foundation (http://www.apache.org/)
-  Version used: 1.9.1
+  Version used: 1.9.2
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
 This project includes the software: org.apache.jclouds.api
   Available at: http://jclouds.apache.org/
   Developed by: The Apache Software Foundation (http://www.apache.org/)
-  Version used: 1.9.1
+  Version used: 1.9.2
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
 This project includes the software: org.apache.jclouds.common
   Available at: http://jclouds.apache.org/
   Developed by: The Apache Software Foundation (http://www.apache.org/)
-  Version used: 1.9.1
+  Version used: 1.9.2
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
 This project includes the software: org.apache.jclouds.driver
   Available at: http://jclouds.apache.org/
   Developed by: The Apache Software Foundation (http://www.apache.org/)
-  Version used: 1.9.1
+  Version used: 1.9.2
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
 This project includes the software: org.apache.jclouds.labs
   Available at: http://jclouds.apache.org/
   Developed by: The Apache Software Foundation (http://www.apache.org/)
-  Version used: 1.9.1
+  Version used: 1.9.2
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
 This project includes the software: org.apache.jclouds.provider
   Available at: http://jclouds.apache.org/
   Developed by: The Apache Software Foundation (http://www.apache.org/)
-  Version used: 1.9.1
+  Version used: 1.9.2
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
 This project includes the software: org.bouncycastle
@@ -678,11 +678,6 @@
   Version used: 3.0.3
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
-This project includes the software: org.python
-  Available at: http://www.jython.org/
-  Version used: 2.7-b3
-  Used under the following license: Jython Software License (http://www.jython.org/license.html)
-
 This project includes the software: org.reflections
   Available at: http://code.google.com/p/reflections/
   Version used: 0.9.9-RC1
@@ -738,11 +733,12 @@
       Arpad Borsos (2012)
     Used under the BSD 2-Clause license.
 
-This project includes the software: swagger
-  Used under the following license: <no license info>
-
-This project includes the software: swagger-ui
-  Used under the following license: <no license info>
+This project includes the software: Swagger UI
+  Available at: https://github.com/swagger-api/swagger-ui
+  Inclusive of: swagger*.{js,css,html}
+  Version used: 2.1.4
+  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+  Copyright (c) SmartBear Software (2011-2015)
 
 This project includes the software: typeahead.js
   Available at: https://github.com/twitter/typeahead.js
diff --git a/brooklyn-dist/pom.xml b/brooklyn-dist/pom.xml
index a2ecb3a..12ebdd9 100644
--- a/brooklyn-dist/pom.xml
+++ b/brooklyn-dist/pom.xml
@@ -76,6 +76,7 @@
         <module>downstream-parent</module>
         <module>all</module>
         <module>dist</module>
+        <module>vagrant</module>
         <module>archetypes/quickstart</module>
     </modules>
 
diff --git a/brooklyn-dist/release/change-version.sh b/brooklyn-dist/release/change-version.sh
index 280c245..4b77749 100755
--- a/brooklyn-dist/release/change-version.sh
+++ b/brooklyn-dist/release/change-version.sh
@@ -66,5 +66,5 @@
     sed -i.bak -e "/${VERSION_MARKER_NL}/{n;s/${CURRENT_VERSION}/${NEW_VERSION}/g;}" $FILES
 fi
 
-echo "Changed ${CURRENT_VERSION} to ${NEW_VERSION} for "${FILES_COUNT}" files"
+echo "Changed ${VERSION_MARKER} from ${CURRENT_VERSION} to ${NEW_VERSION} for "${FILES_COUNT}" files"
 echo "(Do a \`find . -name \"*.bak\" -delete\`  to delete the backup files.)"
diff --git a/brooklyn-dist/release/make-release-artifacts.sh b/brooklyn-dist/release/make-release-artifacts.sh
index 476a6e3..90f138e 100755
--- a/brooklyn-dist/release/make-release-artifacts.sh
+++ b/brooklyn-dist/release/make-release-artifacts.sh
@@ -18,7 +18,10 @@
 # under the License.
 #
 
-# creates a source release - this is a .tar.gz file containing all the source code files that are permitted to be released.
+# Creates the following releases with archives (.tar.gz/.zip), signatures and checksums:
+#   binary  (-bin)     - contains the brooklyn dist binary release
+#   source  (-src)     - contains all the source code files that are permitted to be released
+#   vagrant (-vagrant) - contains a Vagrantfile/scripts to start a Brooklyn getting started environment
 
 set -e
 
@@ -179,7 +182,7 @@
 # * release (where this is running, and people who *have* the release don't need to make it)
 # * jars and friends (these are sometimes included for tests, but those are marked as skippable,
 #     and apache convention does not allow them in source builds; see PR #365
-rsync -rtp --exclude .git\* --exclude docs/ --exclude sandbox/ --exclude release/ --exclude '**/*.[ejw]ar' . ${staging_dir}/${release_name}-src
+rsync -rtp --exclude .git\* --exclude brooklyn-docs/ --exclude brooklyn-library/sandbox/ --exclude brooklyn-dist/release/ --exclude '**/*.[ejw]ar' . ${staging_dir}/${release_name}-src
 
 rm -rf ${artifact_dir}
 mkdir -p ${artifact_dir}
@@ -210,16 +213,29 @@
 # Perform the build and deploy to Nexus staging repository
 ( cd ${src_staging_dir} && mvn deploy -Papache-release )
 ## To test the script without a big deploy, use the line below instead of above
-#( cd ${src_staging_dir} && cd usage/dist && mvn clean install )
+#( cd ${src_staging_dir} && mvn clean install )
 
 # Re-pack the archive with the correct names
-tar xzf ${src_staging_dir}/usage/dist/target/brooklyn-dist-${current_version}-dist.tar.gz -C ${bin_staging_dir}
+tar xzf ${src_staging_dir}/brooklyn-dist/dist/target/brooklyn-dist-${current_version}-dist.tar.gz -C ${bin_staging_dir}
 mv ${bin_staging_dir}/brooklyn-dist-${current_version} ${bin_staging_dir}/${release_name}-bin
 
 ( cd ${bin_staging_dir} && tar czf ${artifact_dir}/${artifact_name}-bin.tar.gz ${release_name}-bin )
 ( cd ${bin_staging_dir} && zip -qr ${artifact_dir}/${artifact_name}-bin.zip ${release_name}-bin )
 
 ###############################################################################
+# Vagrant release
+set +x
+echo "Proceeding to rename and repackage vagrant environment release"
+set -x
+
+# Re-pack the archive with the correct names
+tar xzf ${src_staging_dir}/brooklyn-dist/vagrant/target/brooklyn-vagrant-${current_version}-dist.tar.gz -C ${bin_staging_dir}
+mv ${bin_staging_dir}/brooklyn-vagrant-${current_version} ${bin_staging_dir}/${release_name}-vagrant
+
+( cd ${bin_staging_dir} && tar czf ${artifact_dir}/${artifact_name}-vagrant.tar.gz ${release_name}-vagrant )
+( cd ${bin_staging_dir} && zip -qr ${artifact_dir}/${artifact_name}-vagrant.zip ${release_name}-vagrant )
+
+###############################################################################
 # Signatures and checksums
 
 # OSX doesn't have sha256sum, even if MacPorts md5sha1sum package is installed.
diff --git a/brooklyn-dist/vagrant/pom.xml b/brooklyn-dist/vagrant/pom.xml
new file mode 100644
index 0000000..fbe6539
--- /dev/null
+++ b/brooklyn-dist/vagrant/pom.xml
@@ -0,0 +1,83 @@
+<?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>pom</packaging>
+
+    <artifactId>brooklyn-vagrant</artifactId>
+
+    <name>Brooklyn Vagrant Getting Started Environment</name>
+    <description>
+        Brooklyn Getting Started Vagrant environment archive, includes all required
+        files to start Brooklyn and sample BYON nodes for use.
+    </description>
+
+    <parent>
+        <groupId>org.apache.brooklyn</groupId>
+        <artifactId>brooklyn-dist-root</artifactId>
+        <version>0.9.0-SNAPSHOT</version>  <!-- BROOKLYN_VERSION -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>build-distribution-archive</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <appendAssemblyId>true</appendAssemblyId>
+                            <descriptors>
+                                <descriptor>src/main/config/build-distribution.xml</descriptor>
+                            </descriptors>
+                          <!-- finalName affects name in `target/` but we cannot influence name when it is attached/installed,
+                               so `apache-` prefix would be lost there. to keep it consistent this is commented out, 
+                               but would be nice to have if there is a way!
+                            <finalName>apache-brooklyn-${project.version}</finalName>
+                          -->
+                            <formats>
+                                <format>tar.gz</format>
+                                <format>zip</format>
+                            </formats>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.rat</groupId>
+                    <artifactId>apache-rat-plugin</artifactId>
+                    <configuration>
+                        <excludes combine.children="append">
+                            <exclude>src/main/vagrant/README.md</exclude>
+                        </excludes>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+</project>
diff --git a/brooklyn-dist/vagrant/src/main/config/build-distribution.xml b/brooklyn-dist/vagrant/src/main/config/build-distribution.xml
new file mode 100644
index 0000000..823ad2a
--- /dev/null
+++ b/brooklyn-dist/vagrant/src/main/config/build-distribution.xml
@@ -0,0 +1,33 @@
+<?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 xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+    <id>dist</id>
+    <formats><!-- empty, intended for caller to specify --></formats>
+    <fileSets>
+        <fileSet>
+            <directory>${project.basedir}/src/main/vagrant</directory>
+            <outputDirectory></outputDirectory>
+            <fileMode>0644</fileMode>
+            <directoryMode>0755</directoryMode>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/brooklyn-dist/vagrant/src/main/vagrant/README.md b/brooklyn-dist/vagrant/src/main/vagrant/README.md
new file mode 100644
index 0000000..2f5573c
--- /dev/null
+++ b/brooklyn-dist/vagrant/src/main/vagrant/README.md
@@ -0,0 +1,41 @@
+
+# [![**Brooklyn**](https://brooklyn.apache.org/style/img/apache-brooklyn-logo-244px-wide.png)](http://brooklyn.apache.org/)
+
+### Using Vagrant with Brooklyn -SNAPSHOT builds
+
+##### Install a community-managed version from Maven
+1. No action is required other than setting the  `BROOKLYN_VERSION:` environment variable in `servers.yaml` to a `-SNAPSHOT` version. For example:
+
+   ```
+   env:
+     BROOKLYN_VERSION: 0.9.0-SNAPSHOT
+   ```
+
+2. You may proceed to use the `Vagrantfile` as normal; `vagrant up`, `vagrant destroy` etc.
+
+##### Install a locally built `-dist.tar.gz`
+
+1. Set the `BROOKLYN_VERSION:` environment variable in `servers.yaml` to your current `-SNAPSHOT` version. For example:
+
+   ```
+   env:
+     BROOKLYN_VERSION: 0.9.0-SNAPSHOT
+   ```
+
+2. Set the `INSTALL_FROM_LOCAL_DIST:` environment variable in `servers.yaml` to `true`. For example:
+
+   ```
+   env:
+     INSTALL_FROM_LOCAL_DIST: true
+   ```
+
+
+3. Copy your locally built `-dist.tar.gz` archive to the same directory as the Vagrantfile (this directory is mounted in the Vagrant VM at `/vagrant/`).
+
+   For example to copy a locally built `0.9.0-SNAPSHOT` dist:
+
+   ```
+   cp ~/.m2/repository/org/apache/brooklyn/brooklyn-dist/0.9.0-SNAPSHOT/brooklyn-dist-0.9.0-SNAPSHOT-dist.tar.gz .
+   ```
+
+4. You may proceed to use the `Vagrantfile` as normal; `vagrant up`, `vagrant destroy` etc.
\ No newline at end of file
diff --git a/brooklyn-dist/vagrant/src/main/vagrant/Vagrantfile b/brooklyn-dist/vagrant/src/main/vagrant/Vagrantfile
new file mode 100644
index 0000000..fb35a15
--- /dev/null
+++ b/brooklyn-dist/vagrant/src/main/vagrant/Vagrantfile
@@ -0,0 +1,76 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+#
+# 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.
+#
+
+# Specify minimum Vagrant version and Vagrant API version
+Vagrant.require_version ">= 1.8.1"
+VAGRANTFILE_API_VERSION = "2"
+
+# Update OS (Debian/RedHat based only)
+UPDATE_OS_CMD = "(sudo apt-get update && sudo apt-get -y upgrade) || (sudo yum -y update)"
+
+# Require YAML module
+require 'yaml'
+
+# Read YAML file with box details
+yaml_cfg = YAML.load_file(__dir__ + '/servers.yaml')
+
+# Create boxes
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+
+  # Iterate through server entries in YAML file
+  yaml_cfg["servers"].each do |server|
+    config.vm.define server["name"] do |server_config|
+
+      server_config.vm.box = server["box"]
+
+      server_config.vm.box_check_update = yaml_cfg["default_config"]["check_newer_vagrant_box"]
+
+      if server.has_key?("ip")
+        server_config.vm.network "private_network", ip: server["ip"]
+      end
+
+      if server.has_key?("forwarded_ports")
+        server["forwarded_ports"].each do |ports|
+          server_config.vm.network "forwarded_port", guest: ports["guest"], host: ports["host"], guest_ip: ports["guest_ip"]
+        end
+      end
+
+      server_config.vm.hostname = server["name"]
+      server_config.vm.provider :virtualbox do |vb|
+        vb.name = server["name"]
+        vb.memory = server["ram"]
+        vb.cpus = server["cpus"]
+      end
+
+      if yaml_cfg["default_config"]["run_os_update"]
+        server_config.vm.provision "shell", privileged: false, inline: UPDATE_OS_CMD
+      end
+
+      if server["shell"] && server["shell"]["cmd"]
+        server["shell"]["cmd"].each do |cmd|
+          server_config.vm.provision "shell", privileged: false, inline: cmd, env: server["shell"]["env"]
+        end
+      end
+
+      server_config.vm.post_up_message = server["post_up_message"]
+    end
+  end
+end
\ No newline at end of file
diff --git a/brooklyn-dist/vagrant/src/main/vagrant/files/brooklyn.properties b/brooklyn-dist/vagrant/src/main/vagrant/files/brooklyn.properties
new file mode 100644
index 0000000..0784ff3
--- /dev/null
+++ b/brooklyn-dist/vagrant/src/main/vagrant/files/brooklyn.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+# Disabling security on the Vagrant Brooklyn instance for training purposes
+brooklyn.webconsole.security.provider = org.apache.brooklyn.rest.security.provider.AnyoneSecurityProvider
+
+# Note: BYON locations are loaded from the files/vagrant-catalog.bom on startup
\ No newline at end of file
diff --git a/brooklyn-dist/vagrant/src/main/vagrant/files/brooklyn.service b/brooklyn-dist/vagrant/src/main/vagrant/files/brooklyn.service
new file mode 100644
index 0000000..28b0fea
--- /dev/null
+++ b/brooklyn-dist/vagrant/src/main/vagrant/files/brooklyn.service
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+
+[Unit]
+Description=Apache Brooklyn service
+Documentation=http://brooklyn.apache.org/documentation/index.html
+
+[Service]
+ExecStart=/home/vagrant/apache-brooklyn/bin/brooklyn launch --persist auto --persistenceDir /vagrant/brooklyn-persisted-state --catalogAdd /vagrant/files/vagrant-catalog.bom
+WorkingDirectory=/home/vagrant/apache-brooklyn
+Restart=on-abort
+User=vagrant
+Group=vagrant
+
+[Install]
+WantedBy=multi-user.target
diff --git a/brooklyn-dist/vagrant/src/main/vagrant/files/install_brooklyn.sh b/brooklyn-dist/vagrant/src/main/vagrant/files/install_brooklyn.sh
new file mode 100755
index 0000000..9c52017
--- /dev/null
+++ b/brooklyn-dist/vagrant/src/main/vagrant/files/install_brooklyn.sh
@@ -0,0 +1,92 @@
+#!/usr/bin/env 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.
+#
+
+BROOKLYN_VERSION=""
+INSTALL_FROM_LOCAL_DIST="false"
+TMP_ARCHIVE_NAME=apache-brooklyn.tar.gz
+
+do_help() {
+  echo "./install.sh -v <Brooklyn Version> [-l <install from local file: true|false>]"
+  exit 1
+}
+
+while getopts ":hv:l:" opt; do
+    case "$opt" in
+    v)  BROOKLYN_VERSION=$OPTARG ;;
+        # using a true/false argopt rather than just flag to allow easier integration with servers.yaml config
+    l)  INSTALL_FROM_LOCAL_DIST=$OPTARG ;;
+    h)  do_help;;
+    esac
+done
+
+# Exit if any step fails
+set -e
+
+if [ "x${BROOKLYN_VERSION}" == "x" ]; then
+  echo "Error: you must supply a Brooklyn version [-v]"
+  do_help
+fi
+
+if [ ! "${INSTALL_FROM_LOCAL_DIST}" == "true" ]; then
+  if [ ! -z "${BROOKLYN_VERSION##*-SNAPSHOT}" ] ; then
+    # url for official release versions
+    BROOKLYN_URL="https://www.apache.org/dyn/closer.lua?action=download&filename=brooklyn/apache-brooklyn-${BROOKLYN_VERSION}/apache-brooklyn-${BROOKLYN_VERSION}-bin.tar.gz"
+    BROOKLYN_DIR="apache-brooklyn-${BROOKLYN_VERSION}-bin"
+  else
+    # url for community-managed snapshots
+    BROOKLYN_URL="https://repository.apache.org/service/local/artifact/maven/redirect?r=snapshots&g=org.apache.brooklyn&a=brooklyn-dist&v=${BROOKLYN_VERSION}&c=dist&e=tar.gz"
+    BROOKLYN_DIR="brooklyn-dist-${BROOKLYN_VERSION}"
+  fi
+else
+  echo "Installing from a local -dist archive [ /vagrant/brooklyn-dist-${BROOKLYN_VERSION}-dist.tar.gz]"
+  # url to install from mounted /vagrant dir
+  BROOKLYN_URL="file:///vagrant/brooklyn-dist-${BROOKLYN_VERSION}-dist.tar.gz"
+  BROOKLYN_DIR="brooklyn-dist-${BROOKLYN_VERSION}"
+
+  # ensure local file exists
+  if [ ! -f /vagrant/brooklyn-dist-${BROOKLYN_VERSION}-dist.tar.gz ]; then
+    echo "Error: file not found /vagrant/brooklyn-dist-${BROOKLYN_VERSION}-dist.tar.gz"
+    exit 1
+  fi
+fi
+
+echo "Installing Apache Brooklyn version ${BROOKLYN_VERSION} from [${BROOKLYN_URL}]"
+
+echo "Downloading Brooklyn release archive"
+curl --fail --silent --show-error --location --output ${TMP_ARCHIVE_NAME} "${BROOKLYN_URL}"
+echo "Extracting Brooklyn release archive"
+tar zxf ${TMP_ARCHIVE_NAME}
+
+echo "Creating Brooklyn dirs and symlinks"
+ln -s ${BROOKLYN_DIR} apache-brooklyn
+sudo mkdir -p /var/log/brooklyn
+sudo chown -R vagrant:vagrant /var/log/brooklyn
+mkdir -p /home/vagrant/.brooklyn
+
+echo "Copying default vagrant Brooklyn properties file"
+cp /vagrant/files/brooklyn.properties /home/vagrant/.brooklyn/
+chmod 600 /home/vagrant/.brooklyn/brooklyn.properties
+
+echo "Installing JRE"
+sudo sh -c 'export DEBIAN_FRONTEND=noninteractive; apt-get install --yes openjdk-8-jre-headless'
+
+echo "Copying Brooklyn systemd service unit file"
+sudo cp /vagrant/files/brooklyn.service /etc/systemd/system/brooklyn.service
\ No newline at end of file
diff --git a/brooklyn-dist/vagrant/src/main/vagrant/files/logback.xml b/brooklyn-dist/vagrant/src/main/vagrant/files/logback.xml
new file mode 100644
index 0000000..1560d8b
--- /dev/null
+++ b/brooklyn-dist/vagrant/src/main/vagrant/files/logback.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<configuration scan="true">
+
+    <!-- to supply custom logging, either change this file, supply your own logback-main.xml
+         (overriding the default provided on the classpath) or any of the files it references;
+         see the Logging section of the Brooklyn web site for more information -->
+
+    <property name="logging.basename" scope="context" value="brooklyn" />
+    <property name="logging.dir" scope="context" value="/var/log/brooklyn/" />
+
+    <include resource="logback-main.xml"/>
+
+</configuration>
\ No newline at end of file
diff --git a/brooklyn-dist/vagrant/src/main/vagrant/files/vagrant-catalog.bom b/brooklyn-dist/vagrant/src/main/vagrant/files/vagrant-catalog.bom
new file mode 100644
index 0000000..d8b8450
--- /dev/null
+++ b/brooklyn-dist/vagrant/src/main/vagrant/files/vagrant-catalog.bom
@@ -0,0 +1,82 @@
+#
+# 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:
+  items:
+  - id: byon1
+    name: Vagrant BYON VM 1
+    version: 0.9.0-SNAPSHOT  # BROOKLYN_VERSION
+    itemType: location
+    item:
+      type: byon
+      brooklyn.config:
+        user: vagrant
+        password: vagrant
+        hosts:
+        - 10.10.10.101
+
+  - id: byon2
+    name: Vagrant BYON VM 2
+    version: 0.9.0-SNAPSHOT  # BROOKLYN_VERSION
+    itemType: location
+    item:
+      type: byon
+      brooklyn.config:
+        user: vagrant
+        password: vagrant
+        hosts:
+        - 10.10.10.102
+
+  - id: byon3
+    name: Vagrant BYON VM 3
+    version: 0.9.0-SNAPSHOT  # BROOKLYN_VERSION
+    itemType: location
+    item:
+      type: byon
+      brooklyn.config:
+        user: vagrant
+        password: vagrant
+        hosts:
+        - 10.10.10.103
+
+  - id: byon4
+    name: Vagrant BYON VM 4
+    version: 0.9.0-SNAPSHOT  # BROOKLYN_VERSION
+    itemType: location
+    item:
+      type: byon
+      brooklyn.config:
+        user: vagrant
+        password: vagrant
+        hosts:
+        - 10.10.10.104
+
+  - id: byon-all
+    name: Vagrant BYON VM 1-4
+    version: 0.9.0-SNAPSHOT  # BROOKLYN_VERSION
+    itemType: location
+    item:
+      type: byon
+      brooklyn.config:
+        user: vagrant
+        password: vagrant
+        hosts:
+        - 10.10.10.101
+        - 10.10.10.102
+        - 10.10.10.103
+        - 10.10.10.104
diff --git a/brooklyn-dist/vagrant/src/main/vagrant/servers.yaml b/brooklyn-dist/vagrant/src/main/vagrant/servers.yaml
new file mode 100644
index 0000000..3959fff
--- /dev/null
+++ b/brooklyn-dist/vagrant/src/main/vagrant/servers.yaml
@@ -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.
+#
+#
+# Default Config
+#   check_newer_vagrant_box
+#     enable/disable the vagrant check for an updated box
+#   run_os_update
+#     enable/disable running a yum/apt-get update on box start
+#
+# Brooklyn Server Config
+#   shell:env:BROOKLYN_VERSION
+#     specifies the version of Brooklyn to install, be aware that for SNAPSHOTS you
+#     may wish to download a local -dist.tar.gz for the latest version.
+#   shell:env:INSTALL_FROM_LOCAL_DIST
+#     if set to `true` Vagrant will install from a local -dist.tar.gz stored in /vagrant
+#     on the guest VM (which is mounted from the Vagrantfile directory). You must
+#     ensure that a -dist.tar.gz archive has been copied to this directory on your host.
+
+---
+default_config:
+    check_newer_vagrant_box: true
+    run_os_update: true
+servers:
+  - name: brooklyn
+    box: ubuntu/vivid64
+    ram: 2048
+    cpus: 4
+    ip: 10.10.10.100
+    shell:
+      env:
+        BROOKLYN_VERSION: 0.9.0-SNAPSHOT
+        INSTALL_FROM_LOCAL_DIST: false
+      cmd:
+        - /vagrant/files/install_brooklyn.sh -v ${BROOKLYN_VERSION} -l ${INSTALL_FROM_LOCAL_DIST}
+        - sudo systemctl start brooklyn
+        - sudo systemctl enable brooklyn
+  - name: byon1
+    box: ubuntu/vivid64
+    ram: 512
+    cpus: 2
+    ip: 10.10.10.101
+  - name: byon2
+    box: ubuntu/vivid64
+    ram: 512
+    cpus: 2
+    ip: 10.10.10.102
+  - name: byon3
+    box: ubuntu/vivid64
+    ram: 512
+    cpus: 2
+    ip: 10.10.10.103
+  - name: byon4
+    box: ubuntu/vivid64
+    ram: 512
+    cpus: 2
+    ip: 10.10.10.104
+...
diff --git a/brooklyn-docs/LICENSE b/brooklyn-docs/LICENSE
index ca60394..3d8f4e7 100644
--- a/brooklyn-docs/LICENSE
+++ b/brooklyn-docs/LICENSE
@@ -282,6 +282,13 @@
   Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
   Copyright (c) Vitaly Puzrin (2011-2015)
 
+This project includes the software: marked.js
+  Available at: https://github.com/chjj/marked
+  Developed by: Christopher Jeffrey (https://github.com/chjj)
+  Version used: 0.3.1
+  Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
+  Copyright (c) Christopher Jeffrey (2011-2014)
+
 This project includes the software: moment.js
   Available at: http://momentjs.com
   Developed by: Tim Wood (http://momentjs.com)
@@ -323,17 +330,10 @@
       Arpad Borsos (2012)
     Used under the BSD 2-Clause license.
 
-This project includes the software: Swagger JS
-  Available at: https://github.com/wordnik/swagger-js
-  Inclusive of: swagger.js
-  Version used: 1.0.1
-  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
-  Copyright (c) SmartBear Software (2011-2015)
-
 This project includes the software: Swagger UI
-  Available at: https://github.com/wordnik/swagger-ui
-  Inclusive of: swagger-ui.js
-  Version used: 1.0.1
+  Available at: https://github.com/swagger-api/swagger-ui
+  Inclusive of: swagger*.{js,css,html}
+  Version used: 2.1.4
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
   Copyright (c) SmartBear Software (2011-2015)
 
diff --git a/brooklyn-docs/LICENSE.txt b/brooklyn-docs/LICENSE.txt
deleted file mode 100644
index 99953f7..0000000
--- a/brooklyn-docs/LICENSE.txt
+++ /dev/null
@@ -1,189 +0,0 @@
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this content except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-       (This is included below for reference.)
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
diff --git a/brooklyn-docs/_includes/base-head.html b/brooklyn-docs/_includes/base-head.html
index dc1d31f..0773dc2 100644
--- a/brooklyn-docs/_includes/base-head.html
+++ b/brooklyn-docs/_includes/base-head.html
@@ -7,11 +7,24 @@
 <link href="{% dependency_url bootstrap.css %}" rel="stylesheet">
 <link href="{{site.path.style}}/deps/octicons/octicons.css" rel="stylesheet">
 <link href="{{site.path.style}}/deps/bootstrap-theme.css" rel="stylesheet">
+<link href="{{site.path.style}}/deps/tooltip.css" rel="stylesheet">
 
 <link rel="stylesheet" href="{{ site.path.style }}/css/code.css" type="text/css" media="screen" />
 
 <link href="{{site.path.style}}/css/website.css" rel="stylesheet">
 
 <script src="{% dependency_url jquery.js %}"></script>
+<script src="{% dependency_url glossarizer.js %}"></script>
 <script src="{% dependency_url bootstrap.js %}"></script>
+<script src="{% dependency_url tooltip.js %}"></script>
 <script type="text/javascript" src="{{ site.path.style }}/deps/jquery.cookie.js"></script>
+<script>
+$(function(){
+  $('body').glossarizer({
+    sourceURL: '/guide/glossary.json?'+Math.random(),
+    caseSensitive : true,
+    lookupTagName : 'p, ul',
+    callback: function(){ new tooltip(); }
+  });
+});
+</script>
\ No newline at end of file
diff --git a/brooklyn-docs/_licensing/README b/brooklyn-docs/_licensing/README
new file mode 100644
index 0000000..2fc5f2e
--- /dev/null
+++ b/brooklyn-docs/_licensing/README
@@ -0,0 +1,24 @@
+
+We don't distribute docs as part of the source build so we're not obliged to report the JS we include
+or generate a special LICENSE file as brooklyn-dist/dist/licensing does for the distributed projects.
+
+(This project just gets the usual brooklyn license.)
+
+However we might consider recording the licenses used in the docs.  So we note
+that third-party software bundled by the docs include:
+
+* bloodhound (aka typeahead)
+* glossarizer
+
+And other things used by brooklyn, in style/{deps,js}.
+
+---
+
+This project includes the software: glossarizer
+   Available at: https://github.com/PebbleRoad/glossarizer
+   Developed by: Vinay M, PebbleRoad Pte Ltd (http://www.pebbleroad.com)
+   Version used: 1.5
+   Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
+   Copyright (c) Vinay M, PebbleRoad Pte Ltd (2004-2016)
+ 
+
diff --git a/brooklyn-docs/guide/glossary.json b/brooklyn-docs/guide/glossary.json
new file mode 100644
index 0000000..b201aba
--- /dev/null
+++ b/brooklyn-docs/guide/glossary.json
@@ -0,0 +1,22 @@
+[
+  {
+    "term": "location, !location:",
+    "description": "A server or resource to which Apache Brooklyn can deploy applications"
+  },
+  {
+    "term": "blueprint",
+    "description": "A descriptor or pattern which describes how Apache Brooklyn should deploy applications"
+  },
+  {
+    "term": "entity",
+    "description": "A software package or service Apache Brooklyn can interact with"
+  },
+  {
+    "term": "sensor, !<code>sensor",
+    "description": "A sensor is a property of an Apache Brooklyn entity, updated in real-time"
+  },
+  {
+    "term": "effector",
+    "description": "Effectors are tools Apache Brooklyn provides, that allow you to manipulate the live entities within an application"
+  }
+]
\ No newline at end of file
diff --git a/brooklyn-docs/guide/misc/release-notes.md b/brooklyn-docs/guide/misc/release-notes.md
index 3661ab1..ef79155 100644
--- a/brooklyn-docs/guide/misc/release-notes.md
+++ b/brooklyn-docs/guide/misc/release-notes.md
@@ -16,7 +16,7 @@
 
 ### Introduction
 
-Version 0.8.0 is [TODO add description] 
+Version 0.9.0 is [TODO add description] 
 
 Thanks go to our community for their improvements, feedback and guidance, and
 to Brooklyn's commercial users for funding much of this development.
@@ -24,7 +24,21 @@
 
 ### New Features
 
-[TODO]
+1. Parameters (config keys) can now be defined in YAML, using `brooklyn.parameters`.
+This allows YAML entities to advertise how they should be parameterized,
+for use in the UI and in documentation tools, and do coercion on these values.
+For a good demonstration, see the "Custom Entities" section of the YAML chapter of the user guide. 
+
+[ TODO - 
+restructuring and graduation; 
+the `br` client CLI tool;
+test framework;
+`$brooklyn:external(...)` extension for taking values from other sources is supported in more places;
+better YAML editor in the UI;
+OSGi-native mode using Karaf, to simplify packaging of blueprints;
+a new pure-java WinRM client (winrm4j), hugely reducing the number of dependencies and distro size;
+several version bumps (jclouds) and performance and reliability improvements
+]
  
 
 ### Backwards Compatibility
@@ -49,10 +63,10 @@
 and the referencing catalog item ID also takes priority; this has no effect in most cases, but if you have a chain of
 referenced types blueprint plan source code and the catalog item ID are now set correctly. 
 
-For changes in prior versions, please refer to the release notes for 
-[0.8.0](/v/0.8.0-incubating/misc/release-notes.html).
-
 3. Task cancellation is now propagated to dependent submitted tasks, including backgrounded tasks if they are transient.
 Previously when a task was cancelled the API did not guarantee semantics but the behaviour was to cancel sub-tasks only 
 in very limited cases. Now the semantics are more precise and controllable, and more sub-tasks are cancelled.
 This can prevent some leaked waits on `attributeWhenReady`.
+
+For changes in prior versions, please refer to the release notes for 
+[0.8.0](/v/0.8.0-incubating/misc/release-notes.html).
diff --git a/brooklyn-docs/guide/ops/externalized-configuration.md b/brooklyn-docs/guide/ops/externalized-configuration.md
index ea6c63c..24fd4eb 100644
--- a/brooklyn-docs/guide/ops/externalized-configuration.md
+++ b/brooklyn-docs/guide/ops/externalized-configuration.md
@@ -122,6 +122,23 @@
 {% endhighlight %}
 
 
+## Referring to External Configuration in Catalog Items
+
+The same blueprint language DSL can be used within YAML catalog items. For example:
+
+    brooklyn.catalog:
+      id: com.example.myblueprint
+      version: 1.2.3
+      brooklyn.libraries:
+      - >
+        $brooklyn:formatString("https://%s:%s@repo.example.com/libs/myblueprint-1.2.3.jar", 
+        external("mysuppier", "username"), external("mysupplier", "password"))
+      item:
+        type: com.example.MyBlueprint
+
+Note the `>` in the example above is used to split across multiple lines.
+
+
 ## Suppliers available with Brooklyn
 
 Brooklyn ships with a number of external configuration suppliers ready to use.
diff --git a/brooklyn-docs/guide/ops/security-guidelines.md b/brooklyn-docs/guide/ops/security-guidelines.md
index d8c919d..b6e4460 100644
--- a/brooklyn-docs/guide/ops/security-guidelines.md
+++ b/brooklyn-docs/guide/ops/security-guidelines.md
@@ -40,7 +40,7 @@
 rather than being stored within YAML blueprints or brooklyn.properties.
 
 A secure credential store is strongly recommended, such as use of 
-[HashiCorp's Vault](www.vaultproject.io) - see
+[HashiCorp's Vault](https://www.vaultproject.io) - see
 `org.apache.brooklyn.core.config.external.vault.VaultExternalConfigSupplier`.
 
 
diff --git a/brooklyn-docs/guide/start/blueprints.md b/brooklyn-docs/guide/start/blueprints.md
index 492ac73..7d9a0e2 100644
--- a/brooklyn-docs/guide/start/blueprints.md
+++ b/brooklyn-docs/guide/start/blueprints.md
@@ -6,44 +6,84 @@
 - { section: Launching from a Blueprint, title: Blueprint } 
 ---
 
-<div style="width: 100%; display: block; background-color: #CC9966; margin-bottom: 2px;  padding: 50px 30px 50px 80px;" >
-  <h3>NOTE</h3>
-  <div>
-  The structure of Brooklyn's repositories is changing at present (Jan 2016). Until this is complete 
-  please obtain the "br" command line tool from <a href="https://github.com/brooklyncentral/brooklyn-cli">Brooklyn Central</a>
-  </div>
-</div>
+Blueprints are descriptors or patterns which describe how Apache Brooklyn should deploy applications. Blueprints are written in [YAML](https://en.wikipedia.org/wiki/YAML){:target="_blank"} and all of the entities available are defined in the __[Brooklyn Catalog](../../website/learnmore/catalog/)__.
 
 ## Launching from a Blueprint
 
-We'll start by deploying an application with a simple YAML blueprint containing a Tomcat server.
+We'll start by deploying an application with a simple YAML blueprint containing an [Apache Tomcat](https://tomcat.apache.org/){:target="_blank"} server.
 
-Copy the blueprint below into a text file, "myapp.yaml", in your workspace, but *before* you create an application with 
-it, modify the YAML to specify the location where the application will be deployed.  (Note, to copy the file you can
-hover your mouse over the right side of the text box below to get a Javascript "copy" button.)
+Copy the blueprint below into a text file, "myapp.yaml", in your workspace (Note, to copy the file you can
+hover your mouse over the right side of the text box below to get a Javascript "copy" button).
 
 {% highlight yaml %}
 name: Tomcat
+services:
+- type: org.apache.brooklyn.entity.webapp.tomcat.TomcatServer
+  name: tomcatServer
+location: <your-location-definition-goes-here>
+{% endhighlight %}
+
+
+## Locations
+
+Before you can create an application with this configuration, you need to modify the YAML to specify a location. Locations in Apache Brooklyn are server resources which Brooklyn can use to deploy applications. These locations may be servers or cloud providers which provide access to servers. 
+
+In order to configure the location in which Apache Brooklyn launches an application, replace the ```location:``` element with values for your chosen target environment. Here are some examples of the various location types:
+
+{::options parse_block_html="true" /}
+
+<ul class="nav nav-tabs">
+    <li class="active impl-1-tab"><a data-target="#impl-1, .impl-1-tab" data-toggle="tab" href="#">Vagrant</a></li>
+    <li class="impl-2-tab"><a data-target="#impl-2, .impl-2-tab" data-toggle="tab" href="#">Clouds</a></li>
+    <li class="impl-3-tab"><a data-target="#impl-3, .impl-3-tab" data-toggle="tab" href="#">BYON</a></li>
+</ul>
+
+<div class="tab-content">
+<div id="impl-1" class="tab-pane fade in active">
+
+The Vagrant configuration described in [Running Apache Brooklyn](./running.html), on the previous page is the recommended way of running this tutorial. This configuration comes with four blank vagrant configurations called byon1 to byon4.
+
+These can be launched by entering the following command into the terminal in the vagrant configuration directory.
+
+{% highlight bash %}
+ $ vagrant up byon1 byon2 byon3 byon4
+{% endhighlight %}
+
+The location in "myapp.yaml" can now be replaced with the following YAML to launch using these vagrant servers.
+
+{% highlight yaml %}
+location:
+  byon:
+    user: vagrant
+    password: vagrant
+    hosts:
+      - 10.10.10.101
+      - 10.10.10.102
+      - 10.10.10.103
+      - 10.10.10.104
+{% endhighlight %}
+
+</div>
+<div id="impl-2" class="tab-pane fade">
+
+Apache Brooklyn uses [Apcahe jclouds](http://jclouds.apache.org/){:target="_blank"} to support a range of cloud locations. More information on the range of providers and configurations is available [here](../ops/locations/#clouds){:target="_blank"}.
+
+As an example, here is a configuration for [Amazon Web Services (AWS)](http://www.aws.amazon.com){:target="_blank"}. Swap the identity and credential with your AWS account details, then replace the location in your "myapp.yaml" with this.
+
+{% highlight yaml %}
 location:
   jclouds:aws-ec2:
     identity: ABCDEFGHIJKLMNOPQRST
     credential: s3cr3tsq1rr3ls3cr3tsq1rr3ls3cr3tsq1rr3l
-services:
-- serviceType: brooklyn.entity.webapp.tomcat.TomcatServer
 {% endhighlight %}
 
-Replace the ```location:``` element with values for your chosen target environment, for example to use SoftLayer rather 
-than AWS (updating with your own credentials): 
+</div>
+<div id="impl-3" class="tab-pane fade">
 
-{% highlight yaml %}
-location:
-  jclouds:softlayer:
-    identity: ABCDEFGHIJKLMNOPQRST
-    credential: s3cr3tsq1rr3ls3cr3tsq1rr3ls3cr3tsq1rr3l
-{% endhighlight %}
+The Bring Your Own Nodes (BYON) configuration allows Apache Brooklyn to make use of already available servers. These can be specified by a list of IP addresses with a user and password as shown below. More information including the full range of configuration options is available [here](../ops/locations/#byon){:target="_blank"}. 
 
-Or, if you already have machines provisioned, you can use the "bring your own nodes" (byon) approach. 
-Of course, replace the identity and address values below with your own values.
+Replace the hosts, user and password in the example below with your own server details, then replace the location in your "myapp.yaml" with this.
+
 {% highlight yaml %}
 location:
   byon:
@@ -56,30 +96,36 @@
     - 192.168.0.19
 {% endhighlight %}
 
-**Note**: See __[Locations](../ops/locations)__ in the Operations section of the User Guide for instructions on setting
-up alternate cloud providers, bring-your-own-nodes, or localhost targets, and storing credentials/locations in a file 
-on disk rather than in the blueprint.
-(For the application above, if you are using a "Bring your own Nodes" location, you will need at least three nodes.)
+</div>
+</div>
 
-First you will have to log in to brooklyn:
+---
+
+**Note**: For instructions on setting up a variety of locations or storing credentials/locations in a file on disk rather than in the blueprint, see __[Locations](../ops/locations)__ in the Operations section of the User Guide.
+
+## Deploying the Application
+
+First, log in to brooklyn with the command line interface (CLI) tool by typing:
 {% highlight bash %}
 $ br login http://localhost:8081/
 {% endhighlight %}
 
-To secure the server you can add a username and password in Brooklyn's properties file, as described in the User Guide. 
-Then the login command will require the additional parameters of the userid and password.
+To secure the Apache Brooklyn instance, you can add a username and password to Brooklyn's properties file, as described in the User Guide [here](../ops/brooklyn_properties.html){:target="_blank"}. 
+If this is configured, the login command will require an additional parameter for the userid and will then prompt for a password.
 
 Now you can create the application with the command below:
 
 {% highlight bash %}
-$ br deploy myapp.yaml
+$ br deploy myapp.yaml 
+{% endhighlight %}
+<pre>
 Id:       hTPAF19s   
 Name:     Tomcat   
-Status:   In progress   
-{% endhighlight %}
+Status:   In progress  
+</pre>
 
 Depending on your choice of location it may take some time for the application to start, the next page describes how 
-you can monitor the progress of the application deployment and verify its successful deployment.
+you can monitor the progress of the application deployment and verify if it was successful.
 
 ## Next
 
diff --git a/brooklyn-docs/guide/start/managing.md b/brooklyn-docs/guide/start/managing.md
index da027a7..3724590 100644
--- a/brooklyn-docs/guide/start/managing.md
+++ b/brooklyn-docs/guide/start/managing.md
@@ -13,50 +13,52 @@
 
 
 
-So far we have touched on Brooklyn's ability to *deploy* an application blueprint to a cloud provider, but this just 
-the beginning. The sections below outline how to manage the application that has been deployed.
-
-
-## Scopes in CLI commands
-Many commands require a "scope" expression to indicate the target on which they operate. The scope expressions are
-as follows (values in brackets are aliases for the scope):
-
-- ```application``` APP-ID   (app, a)  
- Selects an application, e.g. "br app myapp"  
-- ```entity```      ENT-ID   (ent, e)  
- Selects an entity within an application scope, e.g. ```br app myapp ent myserver```  
-- ```effector```    EFF-ID   (eff, f)  
- Selects an effector of an entity or application, e.g. ```br a myapp e myserver eff xyz```  
-- ```config```      CONF-KEY (conf, con, c)  
- Selects a configuration key of an entity e.g. ```br a myapp e myserver config jmx.agent.mode```  
-- ```activity```    ACT-ID   (act, v)  
- Selects an activity of an entity e.g. ```br a myapp e myserver act iHG7sq1```  
-
-For example
-{% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r config
-{% endhighlight %}
-runs the ```config``` command with application scope of ```Tomcat``` and entity scope of ```TomcatServer:Wx7r```.
+So far we have gone through Apache Brooklyn's ability to *deploy* an application blueprint to a location, but this just 
+the beginning. Next we will outline how to *manage* the application that has been deployed.
 
 ## Applications
 
-Having created the application we can query its status.  We can find a summary of all deployed apps:
+Having created the application, we can find a summary of all deployed applications using:
 {% highlight bash %}
-$ br application
- Id         Name     Status    Location   
- hTPAF19s   Tomcat   RUNNING   ajVVAhER  
+$ br application  
 {% endhighlight %}
 
-```application``` can be shortened to one of the aliases ```app``` or just ```a```, for example:
-{% highlight bash %}
-$ br app
+<pre>
  Id         Name     Status    Location   
- hTPAF19s   Tomcat   RUNNING   ajVVAhER  
-{% endhighlight %}
+ hTPAF19s   Tomcat   RUNNING   ajVVAhER
+</pre>
 
-You can find the details of a given application, using its name or ID.
+```application``` can be shortened to the alias ```app```, for example:
 {% highlight bash %}
-$ br app Tomcat
+$ br app  
+{% endhighlight %}
+<pre>
+ Id         Name     Status    Location   
+ hTPAF19s   Tomcat   RUNNING   ajVVAhER
+</pre>
+
+A full list of abbreviations such as this can be found in the [CLI reference guide](../ops/cli/cli-ref-guide.html#abbreviations){:target="_blank"}.
+
+In the above example the Id `hTPAF19s` and the Name `Tomcat` are shown. You can use either of these handles to monitor and control the application. The Id shown for your application will be different to this but the name should be the same, note that if you are running multiple applications the Name may not be unique.
+
+#### Things we might want to do
+
+<div class="panel-group" id="accordion">
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <h4 class="panel-title">
+                    <a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">Get the application details</a>
+                </h4>
+            </div>
+            <div id="collapseOne" class="panel-collapse collapse in">
+                <div class="panel-body">
+<p>     
+Using the name `Tomcat` we can get the application details:
+</p>
+{% highlight bash %}
+$ br application Tomcat
+{% endhighlight %}
+<pre>
   Id:              hTPAF19s   
   Name:            Tomcat   
   Status:          RUNNING   
@@ -67,68 +69,112 @@
   LocationName:    FixedListMachineProvisioningLocation:ajVV   
   LocationSpec:    vagrantbyon   
   LocationType:    org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation  
-{% endhighlight %}
-
-To ease management of multiple applications, or even to reduce the amount of typing required, it is convenient
-to create an alias for the commonly used application scope:
-{% highlight bash %}
-alias tom="br app Tomcat"
-{% endhighlight %}
-
-To illustrate this we will assume the above alias for the rest of this section, but to avoid confusion 
-the examples in other sections will show the full command in all cases.
-
+</pre>        
+                </div>
+            </div>
+        </div>
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <h4 class="panel-title">
+                    <a data-toggle="collapse" data-parent="#accordion" href="#collapseTwo">Explore the hierarchy of all applications</a>
+                </h4>
+            </div>
+            <div id="collapseTwo" class="panel-collapse collapse">
+                <div class="panel-body">
+<p>              
 We can explore the management hierarchy of all applications, which will show us the entities they are composed of.
+</p>
 {% highlight bash %}
 $ br tree
+{% endhighlight %}
+<pre>
 |- Tomcat
 +- org.apache.brooklyn.entity.stock.BasicApplication
-  |- TomcatServer:Wx7r
+  |- tomcatServer
   +- org.apache.brooklyn.entity.webapp.tomcat.TomcatServer
-{% endhighlight %}
-
+</pre>
+                </div>
+            </div>
+        </div>
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <h4 class="panel-title">
+                    <a data-toggle="collapse" data-parent="#accordion" href="#collapseThree">View our application's blueprint</a>
+                </h4>
+            </div>
+            <div id="collapseThree" class="panel-collapse collapse">
+                <div class="panel-body">
+<p>
 You can view the blueprint for the application again:
+</p>
 {% highlight bash %}
-$ tom spec
-"name: Tomcat\nlocation:\n  mylocation\nservices:\n- serviceType: brooklyn.entity.webapp.tomcat.TomcatServer\n"
+$ br application Tomcat spec
 {% endhighlight %}
-
-You can view the config of the application:
+<pre>
+"name: Tomcat\nlocation:\n  mylocation\nservices:\n- serviceType: brooklyn.entity.webapp.tomcat.TomcatServer\n"
+</pre>                </div>
+            </div>
+        </div>
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <h4 class="panel-title">
+                    <a data-toggle="collapse" data-parent="#accordion" href="#collapseFour">View our application's configuration</a>
+                </h4>
+            </div>
+            <div id="collapseFour" class="panel-collapse collapse">
+                <div class="panel-body">
+<p>
+You can view the configuration of the application:
+</p>
 {% highlight bash %}
-$ tom config
+$ br application Tomcat config
+{% endhighlight %}
+<pre>
 Key                    Value   
 camp.template.id       l67i25CM   
 brooklyn.wrapper_app   true   
-{% endhighlight %}
+</pre>
+                </div>
+            </div>
+        </div>
+    </div>
 
 ## Entities
-To explore the entities of the application you can use the ```entity``` command. This will show the 
-immediate child entities of a given application or one of its child entities.
+
+An *Entity* is Apache Brooklyn's representation of a software package or service which it can control or interact with. All of the entities Apache Brooklyn can use are listed in the __[Brooklyn Catalog](../../website/learnmore/catalog/)__. 
+
+To list the entities of the application you can use the `entity` or `ent` command:
 
 {% highlight bash %}
-$ br app Tomcat entity
-Id         Name                Type   
-Wx7r1C4e   TomcatServer:Wx7r   org.apache.brooklyn.entity.webapp.tomcat.TomcatServer      
+$ br application Tomcat entity
 {% endhighlight %}
+<pre>
+Id         Name                Type   
+Wx7r1C4e   tomcatServer   org.apache.brooklyn.entity.webapp.tomcat.TomcatServer      
+</pre>
 
-```entity``` has aliases ```ent``` or ```e```.
+This shows one entity is available: `tomcatServer`. Note that this is the name we gave the entity in the YAML in [Launching from a Blueprint](./blueprints.html#launching-from-a-blueprint) on the previous page.
 
-You can get summary information for an entity by providing its name (or ID).
+You can get summary information for this entity by providing its name (or ID).
 
 {% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r
+$ br application Tomcat entity tomcatServer
+{% endhighlight %}
+<pre>
 Id:              Wx7r1C4e   
-Name:            TomcatServer:Wx7r   
+Name:            tomcatServer   
 Status:          RUNNING   
 ServiceUp:       true   
 Type:            org.apache.brooklyn.entity.webapp.tomcat.TomcatServer   
 CatalogItemId:   null   
-{% endhighlight %}
+</pre>
 
-Also you can see the config of the entity with the ```config``` command.
+Also you can see the configuration of this entity with the ```config``` command.
 
 {% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r config
+$ br application Tomcat entity tomcatServer config
+{% endhighlight %}
+<pre>
 Key                       Value   
 jmx.agent.mode            JMXMP_AND_RMI   
 brooklyn.wrapper_app      true   
@@ -136,35 +182,32 @@
 onbox.base.dir            /home/vagrant/brooklyn-managed-processes   
 onbox.base.dir.resolved   true   
 install.unique_label      TomcatServer_7.0.65   
-{% endhighlight %}
-
-If an entity name is annoyingly long to type, the entity can be renamed:
-
-{% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r rename server
-{% endhighlight %}
+</pre>
 
 ## Sensors
 
-"Sensors" on entities provide a real-time picture of the status and operation of an entity of the application.
+*Sensors* are properties which show the state of an *entity* and provide a real-time picture of an *entity* within an application.
 
-To view the sensors on the application itself, use the command below:
+You can view the sensors available on the application using:
 
 {% highlight bash %}
-$ br app Tomcat sensor
+$ br application Tomcat sensor
+{% endhighlight %}
+<pre>
 Name                       Description                                                                             Value   
 service.isUp               Whether the service is active and availability (confirmed and monitored)                true   
 service.notUp.indicators   A map of namespaced indicators that the service is not up                               {}   
 service.problems           A map of namespaced indicators of problems with a service                               {}   
 service.state              Actual lifecycle state of the service                                                   "RUNNING"   
 service.state.expected     Last controlled change to service state, indicating what the expected state should be   "running @ 1450356994928 / Thu Dec 17 12:56:34 GMT 2015"
-{% endhighlight %}
+</pre>
 
-To explore all sensors available on an entity use the sensor command with an entity scope.
-Note, again, the name of the application or entity can be used or the ID:
+To explore sensors on a specific entity use the `sensor` command with an entity specified:
 
 {% highlight bash %}
-br app Tomcat ent TomcatServer:Wx7r sensor
+$ br application Tomcat entity tomcatServer sensor
+{% endhighlight %}
+<pre>
 Name                                            Description                                                                                                      Value   
 download.addon.urls                             URL patterns for downloading named add-ons (will substitute things like ${version} automatically)                   
 download.url                                    URL pattern for downloading the installer (will substitute things like ${version} automatically)                 "http://download.nextag.com/apache/tomcat/tomcat-7/v${version}/bin/apache-tomcat-${version}.tar.gz"   
@@ -175,34 +218,42 @@
 host.subnet.address                             Host address as known internally in the subnet where it is running (if different to host.name)                   "10.10.10.101"   
 host.subnet.hostname                            Host name as known internally in the subnet where it is running (if different to host.name)                      "10.10.10.101"   
 # etc. etc.
-{% endhighlight %}
+</pre>
 
 
-To study selected sensors, give the command the sensor name as an argument
+To display the value of a selected sensor, give the command the sensor name as an argument
 
 {% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r sensor webapp.url   
-"http://10.10.10.101:8080/"
+$ br application Tomcat entity tomcatServer sensor webapp.url  
 {% endhighlight %}
+<pre>
+"http://10.10.10.101:8080/"
+</pre>
 
 
 ## Effectors
 
-Effectors are the means by which you can manipulate the entities in an application.  For an application you can list them 
-with 
+Effectors are a means by which you can manipulate the entities in an application.  You can list the available effectors for your application using:
 
 {% highlight bash %}
-$ br app Tomcat effector
+$ br application Tomcat effector
+{% endhighlight %}
+<pre>
 Name            Description                                                                                                                                                                            Parameters   
 restart         Restart the process/service represented by an entity                                                                                                                                      
 start           Start the process/service represented by an entity                                                                                                                                     locations   
 stop            Stop the process/service represented by an entity                                                                                                                                         
-{% endhighlight %}
+</pre>
 
-For an entity supply the entity scope:
+For example, to stop an application, use the ```stop``` effector. This will cleanly shutdown all components in the application and return any cloud machines that were being used. 
+Note that the three "lifecycle" related effectors, ```start```, ```stop```, and ```restart```, are common to all applications and software process entities in Brooklyn.
+
+You can list the effectors for a specific entity using the command:
 
 {% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r effector
+$ br application Tomcat entity tomcatServer effector
+{% endhighlight %}
+<pre>
 Name                              Description                                                                               Parameters   
 deploy                            Deploys the given artifact, from a source URL, to a given deployment filename/context     url,targetName   
 populateServiceNotUpDiagnostics   Populates the attribute service.notUp.diagnostics, with any available health indicators      
@@ -210,82 +261,63 @@
 start                             Start the process/service represented by an entity                                        locations   
 stop                              Stop the process/service represented by an entity                                         stopProcessMode,stopMachineMode   
 undeploy                          Undeploys the given context/artifact                                                      targetName   
-{% endhighlight %}
+</pre>
 
-To view just one effector's documentation, supply its name to the show command:
+To view the details for a specific effector, append it's name to the command:
 
 {% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r effector deploy
-Name            Description                                                                                                                                                                            Parameters   
-deploy          Deploys the given artifact, from a source URL, to a given deployment filename/context                                                                                                  url,targetName   
+$ br application Tomcat entity tomcatServer effector deploy
 {% endhighlight %}
-
-These effectors can be invoked using the command ```invoke```, supplying the application and entity id of the entity to 
-invoke the effector on.   
-
-For example, to stop an application, use the ```stop``` effector. This will cleanly shutdown all components in the 
-application and return any cloud machines that were being used. Do the invocation by supplying the effector name in 
-the scope, and using the command ```invoke```. 
-
-{% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r eff stop invoke
-{% endhighlight %}
-
-Note that the three "lifecycle" related effectors, ```start```, ```stop```, and ```restart```, are common to all software process 
-entities in Brooklyn. They are so commonly used that they have their own aliases. The above could also have been done
-by:
-
-{% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r stop
-{% endhighlight %}
-
-Some effectors require parameters for their invocation, as in the example of ```deploy``` above.  
-
-{% highlight bash %}
-br app Tomcat ent TomcatServer:Wx7r effector deploy
+<pre>
 Name     Description                                                                             Parameters   
 deploy   Deploys the given artifact, from a source URL, to a given deployment filename/context   url,targetName   
-{% endhighlight %}
+</pre>
 
-Now the effector can be invoked by supplying the parameters using ```--param parm=value``` or just ```-P parm=value```.
+These effectors can also be invoked by appending ```invoke``` to this command. Some effectors require parameters for their invocation. For example, if we look at the details for ```deploy``` above we can see it requires a url and targetName. 
 
-In the example below, a sample Tomcat war file is deployed, a variable is created for the root URL using the appropriate
-sensor, and the index page is fetched. Note that at present a ```tr``` command is required in the second line below to strip
-quotation characters from the returned sensor value. 
+These parameters can be supplied using ```--param parm=value``` or just ```-P parm=value```. 
+
+The commands below deploy the Apache Tomcat [hello world example](http://tomcat.apache.org/tomcat-6.0-doc/appdev/index.html){:target="_blank"} to our Tomcat Server. In these commands, a variable is created for the root URL using the appropriate
+sensor and the index page html is displayed. 
 
 {% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r effector deploy invoke -P url=https://tomcat.apache.org/tomcat-6.0-doc/appdev/sample/sample.war -P targetName=sample
-$ webapp=$(br app Tomcat ent TomcatServer:Wx7r sensor webapp.url | tr -d '"')
+$ br application Tomcat entity tomcatServer effector deploy invoke -P url=https://tomcat.apache.org/tomcat-6.0-doc/appdev/sample/sample.war -P targetName=sample
+$ webapp=$(br application Tomcat entity tomcatServer sensor webapp.url | tr -d '"')
 $ curl $webapp/sample/
-<html>
-<head>
-<title>Sample "Hello, World" Application</title>
-</head>
-# etc. etc.
 {% endhighlight %}
+    <html>
+    <head>
+    <title>Sample "Hello, World" Application</title>
+    </head>
+    ...
 
+**Note** that at present a ```tr``` command is required in the second line below to strip quotation characters from the returned sensor value. 
 
 ## Activities
 
-The ```activity``` command allows us to investigate the activities of an entity. 
+*Activities* are the actions an application or entity takes within Apache Brooklyn. The ```activity``` command allows us to list out these activities. 
 
-To view a list of all activities associated with an entity simply use
+To view a list of all activities associated with an entity enter:
 
 {% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r activity
+$ br application Tomcat entity tomcatServer activity
+{% endhighlight %}
+<pre>
 Id         Task                                       Submitted                      Status      Streams   
 LtD5P1cb   start                                      Thu Dec 17 15:04:43 GMT 2015   Completed   
 l2qo4vTl   provisioning (FixedListMachineProvisi...   Thu Dec 17 15:04:43 GMT 2015   Completed   
 wLD764HE   pre-start                                  Thu Dec 17 15:04:43 GMT 2015   Completed    
 KLTxDkoa   ssh: initializing on-box base dir ./b...   Thu Dec 17 15:04:43 GMT 2015   Completed   env,stderr,stdin,stdout   
 jwwcJWmF   start (processes)                          Thu Dec 17 15:04:43 GMT 2015   Completed        
-# etc. etc.
-{% endhighlight %}
+...
+</pre>
 
-To view the details of an individual activity provide its ID:
+To view the details of an individual activity, add its ID to the command. In our case this is `jwwcJWmF`
 
 {% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r activity jwwcJWmF
+$ br application Tomcat entity tomcatServer activity jwwcJWmF
+{% endhighlight %}
+<pre>
 Id:                  jwwcJWmF   
 DisplayName:         start (processes)   
 Description:            
@@ -302,15 +334,114 @@
 DetailedStatus:      "Completed after 4m 16s
 
 No return value (null)"   
-{% endhighlight %}
+</pre>
 
-If an activity has failed, the "DetailedStatus" value will show information about the failure, as an aid to diagnosis.
 
-Adding the ```--children``` or ```-c``` parameter will show the activity's child activities, to allow the hierarchical structure 
-of the activities to be investigated:
+#### Things we might want to do
 
+<div class="panel-group" id="accordionB">
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <h4 class="panel-title">
+                    <a data-toggle="collapse" data-parent="#accordionB" href="#collapseOneB">View Input and Output Streams</a>
+                </h4>
+            </div>
+            <div id="collapseOneB" class="panel-collapse collapse in">
+                <div class="panel-body">
+<p>
+If an activity has associated input and output streams, these may be viewed by providing the activity scope and
+using the commands, "env", "stdin", "stdout", and "stderr".  For example, for the "initializing on-box base dir"
+activity from the result of the earlier example,
+</p>
 {% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r activity -c jwwcJWmF
+$ br application Tomcat entity tomcatServer act KLTxDkoa stdout
+{% endhighlight %} 
+<pre>
+BASE_DIR_RESULT:/home/vagrant/brooklyn-managed-processes:BASE_DIR_RESULT
+</pre>
+                </div>
+            </div>
+        </div>
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <h4 class="panel-title">
+                    <a data-toggle="collapse" data-parent="#accordionB" href="#collapseTwoB">Monitor the progress of an effector</a>
+                </h4>
+            </div>
+            <div id="collapseTwoB" class="panel-collapse collapse">
+                <div class="panel-body">
+                        
+<p>       
+To monitor progress on an application as it deploys, for example, one could use a shell loop:
+</p>
+{% highlight bash %}
+$ while br application Tomcat entity tomcatServer activity | grep 'In progress' ; do 
+  sleep 1; echo ; date; 
+done
+{% endhighlight %}
+<p>
+This loop will exit when the application has deployed successfully or has failed.  If it fails then the 'stderr' 
+command may provide information about what happened in any activities that have associated streams:
+</p>
+{% highlight bash %}
+$ br application Tomcat entity tomcatServer act KLTxDkoa stderr
+{% endhighlight %}                      
+                
+                </div>
+            </div>
+        </div>
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <h4 class="panel-title">
+                    <a data-toggle="collapse" data-parent="#accordionB" href="#collapseThreeB">Diagnose a failure</a>
+                </h4>
+            </div>
+            <div id="collapseThreeB" class="panel-collapse collapse">
+                <div class="panel-body">
+                
+<p>
+If an activity has failed, the "DetailedStatus" value will help us diagnose what went wrong by showing information about the failure.
+</p>
+{% highlight bash %}
+$ br application evHUlq0n entity tomcatServer activity lZZ9x662
+{% endhighlight %}
+<pre>
+Id:                  lZZ9x662   
+DisplayName:         post-start   
+Description:            
+EntityId:            qZeyoITy   
+EntityDisplayName:   tomcatServer   
+Submitted:           Mon Jan 25 12:54:55 GMT 2016   
+Started:             Mon Jan 25 12:54:55 GMT 2016   
+Ended:               Mon Jan 25 12:59:56 GMT 2016   
+CurrentStatus:       Failed   
+IsError:             true   
+IsCancelled:         false   
+SubmittedByTask:     hWU7Qvgm   
+Streams:                
+DetailedStatus:      "Failed after 5m: Software process entity TomcatServerImpl{id=qZeyoITy} did not pass is-running check within the required 5m limit (5m elapsed)
+
+java.lang.IllegalStateException: Software process entity TomcatServerImpl{id=qZeyoITy} did not pass is-running check within the required 5m limit (5m elapsed)
+	at org.apache.brooklyn.entity.software.base.SoftwareProcessImpl.waitForEntityStart(SoftwareProcessImpl.java:586)
+	at org.apache.brooklyn.entity.software.base.SoftwareProcessImpl.postDriverStart(SoftwareProcessImpl.java:260)
+	at org.apache.brooklyn.entity.software.base.SoftwareProcessDriverLifecycleEffectorTasks.postStartCustom(SoftwareProcessDriverLifecycleEffectorTasks.java:169)
+	at org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks$PostStartTask.run(MachineLifecycleEffectorTasks.java:570)
+	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
+	at org.apache.brooklyn.util.core.task.DynamicSequentialTask$DstJob.call(DynamicSequentialTask.java:342)
+	at org.apache.brooklyn.util.core.task.BasicExecutionManager$SubmissionCallable.call(BasicExecutionManager.java:468)
+	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
+	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
+	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
+	at java.lang.Thread.run(Thread.java:745)"
+</pre>
+<p>
+Adding the "--children" or "-c" parameter will show the activity's child activities, to allow the hierarchical structure 
+of the activities to be investigated:
+</p>
+{% highlight bash %}
+$ br application Tomcat entity tomcatServer activity -c jwwcJWmF
+{% endhighlight %}
+<pre>
 Id         Task                         Submitted                      Status   
 UpYRc3fw   copy-pre-install-resources   Thu Dec 17 15:04:43 GMT 2015   Completed   
 ig8sBHQr   pre-install                  Thu Dec 17 15:04:43 GMT 2015   Completed   
@@ -325,33 +456,37 @@
 HKrYfH6h   launch                       Thu Dec 17 15:08:58 GMT 2015   Completed   
 T1m8VXbq   post-launch-command          Thu Dec 17 15:08:59 GMT 2015   Completed   
 n8eK5USE   post-launch                  Thu Dec 17 15:08:59 GMT 2015   Completed   
-{% endhighlight %}
+</pre>                
+                      
+                </div>
+            </div>
+        </div>
+    </div>
 
-If an activity has associated input and output streams, these may be viewed by providing the activity scope and
-using the commands, ```env```, ```stdin```, ```stdout```, and ```stderr```.  For example, for the "initializing on-box base dir"
-activity from the result of the earlier example,
 
+{::comment}
+## Scopes in CLI commands
+Many commands require a "scope" expression to indicate the target on which they operate. The scope expressions are
+as follows (values in brackets are aliases for the scope):
+
+- ```application``` APP-ID   (app, a)  
+ Selects an application, e.g. "br application myapp"  
+- ```entity```      ENT-ID   (ent, e)  
+ Selects an entity within an application scope, e.g. ```br application myapp entity myserver```  
+- ```effector```    EFF-ID   (eff, f)  
+ Selects an effector of an entity or application, e.g. ```br a myapp e myserver eff xyz```  
+- ```config```      CONF-KEY (conf, con, c)  
+ Selects a configuration key of an entity e.g. ```br a myapp e myserver config jmx.agent.mode```  
+- ```activity```    ACT-ID   (act, v)  
+ Selects an activity of an entity e.g. ```br a myapp e myserver act iHG7sq1```  
+
+For example
 {% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r act KLTxDkoa stdout
-BASE_DIR_RESULT:/home/vagrant/brooklyn-managed-processes:BASE_DIR_RESULT
-
+$ br application Tomcat entity tomcatServer config
 {% endhighlight %}
+runs the ```config``` command with application scope of ```Tomcat``` and entity scope of ```tomcatServer```.
 
-
-To monitor progress on an application as it deploys, for example, one could use a shell loop:
-
-{% highlight bash %}
-$ while br app Tomcat ent TomcatServer:Wx7r activity | grep 'In progress' ; do 
-  sleep 1; echo ; date; 
-done
-{% endhighlight %}
-This loop will exit when the application has deployed successfully or has failed.  If it fails then the 'stderr' 
-command may provide information about what happened in any activities that have associated streams:
-
-{% highlight bash %}
-$ br app Tomcat ent TomcatServer:Wx7r act KLTxDkoa stderr
-{% endhighlight %}
-
+{:/comment}
 
 ## Next
 
diff --git a/brooklyn-docs/guide/start/running.md b/brooklyn-docs/guide/start/running.md
index 54315ec..5da792c 100644
--- a/brooklyn-docs/guide/start/running.md
+++ b/brooklyn-docs/guide/start/running.md
@@ -7,33 +7,64 @@
 
 This guide will walk you through deploying an example 3-tier web application to a public cloud, and demonstrate the autoscaling capabilities of the Brooklyn platform.
 
-An overview of core [Brooklyn concepts](./concept-quickstart.html) is available for reference.
+An overview of core [Brooklyn concepts](./concept-quickstart.html){:target="_blank"} is available for reference.
 
-This tutorial assumes that you are using Linux or Mac OS X.
+Two methods of deployment are detailed in this tutorial, using virtualisation with Vagrant and a local install. Both assume that you are using Linux or Mac OS X.
 
 ## Install Apache Brooklyn
 
-Download the Apache Brooklyn binary distribution as described on [the download page]({{site.path.website}}/download/).
+{::options parse_block_html="true" /}
+
+<ul class="nav nav-tabs">
+    <li class="active impl-1-tab"><a data-target="#impl-1, .impl-1-tab" data-toggle="tab" href="#">Vagrant</a></li>
+    <li class="impl-2-tab"><a data-target="#impl-2, .impl-2-tab" data-toggle="tab" href="#">Local Install</a></li>
+</ul>
+
+<div class="tab-content">
+<div id="impl-1" class="tab-pane fade in active">
+
+[Vagrant](https://www.vagrantup.com/){:target="_blank"} is a software package which automates the process of setting up virtual environments such as [Oracle VirtualBox](https://www.virtualbox.org){:target="_blank"}. We recommend it as
+the easiest way of getting started with Apache Brooklyn.
+
+Firstly, download and install:
+
+ * [Vagrant](http://www.vagrantup.com/downloads){:target="_blank"}
+ * [Oracle VirtualBox](https://www.virtualbox.org/wiki/Downloads){:target="_blank"}
+ 
+Then download the default Apache Brooklyn vagrant configuration from [here](https://github.com/johnmccabe/vagrant-brooklyn-getting-started/archive/master.tar.gz){:target="_blank"}. This configuration contains everything you need to get started using Apache Brooklyn.
+
+Extract the `tar.gz` archive and navigate into the expanded `vagrant-brooklyn-getting-started-master` folder
+
+{% highlight bash %}
+$ tar xvf master.tar.gz
+$ cd vagrant-brooklyn-getting-started-master
+{% endhighlight %}
+
+
+</div>
+<div id="impl-2" class="tab-pane fade">
+
+Download the Apache Brooklyn binary distribution as described on [the download page]({{site.path.website}}/download/){:target="_blank"}.
 
 {% if brooklyn_version contains 'SNAPSHOT' %}
-Expand the `tar.gz` archive (note: as this is a -SNAPSHOT version, your filename will be slightly different):
+Extract the `tar.gz` archive (note: as this is a -SNAPSHOT version, your filename will be slightly different):
 {% else %}
-Expand the `tar.gz` archive:
+Extract the `tar.gz` archive and navigate into the expanded `apache-brooklyn-{{ site.brooklyn-version }}` folder.
 {% endif %}
 
 {% if brooklyn_version contains 'SNAPSHOT' %}
 {% highlight bash %}
 $ tar -zxf apache-brooklyn-dist-{{ site.brooklyn-version }}-timestamp-dist.tar.gz
+$ cd apache-brooklyn-{{ site.brooklyn.version }}
 {% endhighlight %}
 {% else %}
 {% highlight bash %}
 $ tar -zxf apache-brooklyn-{{ site.brooklyn-version }}-dist.tar.gz
+$ cd apache-brooklyn-{{ site.brooklyn.version }}
 {% endhighlight %}
 {% endif %}
 
-This will create a `apache-brooklyn-{{ site.brooklyn-version }}` folder.
-
-**Note**: You'll need a Java JRE or SDK installed (version 7 or later), as Brooklyn is Java under the covers.
+**Note**: You'll need a [Java Runtime Environment (JRE)](https://www.java.com){:target="_blank"} installed (version 7 or later), as Brooklyn is Java under the covers.
 
 It is not necessary at this time, but depending on what you are going to do, 
 you may wish to set up other configuration options first:
@@ -42,24 +73,63 @@
 * [Persistence](../ops/persistence/)
 * [Cloud credentials](../ops/locations/)
 
+</div>
+</div>
+
+---
+
 ## Launch Apache Brooklyn
 
-Now start Brooklyn with the following command:
+<ul class="nav nav-tabs">
+    <li class="active impl-1-tab"><a data-target="#impl-1, .impl-1-tab" data-toggle="tab" href="#">Vagrant</a></li>
+    <li class="impl-2-tab"><a data-target="#impl-2, .impl-2-tab" data-toggle="tab" href="#">Local Install</a></li>
+</ul>
+
+<div class="tab-content">
+<div id="impl-1" class="tab-pane fade in active">
+
+Now start Apache Brooklyn with the following command:
 
 {% highlight bash %}
-$ cd apache-brooklyn-{{ site.brooklyn.version }}
+$ vagrant up brooklyn
+{% endhighlight %}
+
+You can see if Apache Brooklyn launched OK by viewing the log files with the command
+
+{% highlight bash %}
+$ vagrant ssh brooklyn --command 'sudo journalctl -n15 -f -u brooklyn'
+{% endhighlight %}
+
+</div>
+<div id="impl-2" class="tab-pane fade">
+
+Now start Apache Brooklyn with the following command:
+
+{% highlight bash %}
 $ bin/brooklyn launch
 {% endhighlight %}
 
-Brooklyn will output the address of the management interface:
+The application should then output it's log into the console
+
+</div>
+</div>
+
+---
+
+## Control Apache Brooklyn
+
+Apache Brooklyn has a web console which can be used to control the application. The Brooklyn log will contain the address of the management interface:
 
 <pre>
-INFO  Starting brooklyn web-console on loopback interface because no security config is set
-INFO  Started Brooklyn console at http://127.0.0.1:8081/, running classpath://brooklyn.war and []
+INFO  Started Brooklyn console at http://127.0.0.1:8081/, running classpath://brooklyn.war
 </pre>
 
-### Next
+By default it can be accessed by opening [127.0.0.1:8081](http://127.0.0.1:8081){:target="_blank"} in your web browser. 
 
-Next, open the web console on [127.0.0.1:8081](http://127.0.0.1:8081). 
-No applications have been deployed yet, so the "Create Application" dialog opens automatically:
-let's remedy this by **[deploying a blueprint](blueprints.html)**.
\ No newline at end of file
+The rest of this getting started guide uses the Apache Brooklyn command line interface (CLI). To use this, download and install the tool as described on the [CLI GitHub page](https://github.com/brooklyncentral/brooklyn-cli){:target="_blank"}.
+
+The CLI provides the command `br`, it's full usage is described in the user manual which can be found [here](../ops/cli/){:target="_blank"}
+
+## Next
+
+The first thing we want to do with Brooklyn is **[deploy a blueprint](blueprints.html)**.
diff --git a/brooklyn-docs/guide/yaml/custom-entities.md b/brooklyn-docs/guide/yaml/custom-entities.md
index 6f21c14..9698aa8 100644
--- a/brooklyn-docs/guide/yaml/custom-entities.md
+++ b/brooklyn-docs/guide/yaml/custom-entities.md
@@ -24,15 +24,26 @@
 {% endhighlight %}
 
 This starts a simple `nc` listener on port 4321 which will respond `hello` to the first
-session which connects to it. Test it by running `telnet localhost 4321`.  
+session which connects to it. Test it by running `telnet localhost 4321`
+or opening `http://localhost:4321` in a browser.
+
+Note that it only allows you connect once, and after that it fails.
+This is deliberate! We'll repair this later in this example.
+Until then however, in the *Applications* view you can click the server,
+go to the `Effectors` tab, and click `restart` to bring if back to life.  
 
 This is just a simple script, but it shows how any script can be easily embedded here,
 including a script to download and run other artifacts.
 Many artifacts are already packaged such that they can be downloaded and launched 
 with a simple script, and `VanillaSoftwareProcess` can also be used for them. 
-We can specify a `download.url` which downloads artifacts (unpacking TAR, TGZ, and ZIP archives)
+
+
+#### Downloading Files
+
+We can specify a `download.url` which downloads an artifact 
+(and automatically unpacking TAR, TGZ, and ZIP archives)
 before running `launch.command` relative to where that file is installed (or unpacked),
-with `./start.sh` being the default `launch.command`.
+with the default `launch.command` being `./start.sh`.
 
 So if we create a file `/tmp/netcat-server.tgz` containing just `start.sh` in the root
 which consists of the two lines in the previous example,
@@ -42,17 +53,130 @@
 {% readj example_yaml/vanilla-bash-netcat-file.yaml %}
 {% endhighlight %}
 
-The one requirement of the script is that it store the process ID (PID) in the file
+
+#### Port Inferencing
+
+If you're deploying to a cloud machine, a firewall might block the port 4321.
+We can tell Brooklyn to open this port explicitly by specifying `inboundPorts: [ 4321 ]`;
+however a more idiomatic way is to specify a config ending with `.port`,
+such as:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-port.yaml %}
+{% endhighlight %}
+
+The regex for ports to be opened can be configured using
+the config `inboundPorts.configRegex` (which has `.*\.port` as the default value).
+
+Config keys of type `org.apache.brooklyn.api.location.PortRange` (aka `port`)
+have special behaviour: when configuring, you can use range notation `8000-8100` or `8000+` to tell Brooklyn
+to find **one** port matching; this is useful when ports might be in use.
+In addition, any such config key will be opened, 
+irrespective of whether it matches the `inboundPorts.configRegex`. 
+To prevent any inferencing of ports to open, you can set the config `inboundPorts.autoInfer` to `false`.
+
+Note that in the example above, `netcat.port` must be specified in a `brooklyn.config` block.
+This block can be used to hold any config (including for example the `launch.command`),
+but for convenience Brooklyn allows config keys declared on the underlying type
+to be specified up one level, alongside the type.
+However config keys which are *not* declared on the type *must* be declared in the `brooklyn.config` block. 
+
+
+#### Declaring New Config Keys
+
+We can define config keys to be presented to the user 
+using the `brooklyn.parameters` block:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-port-parameter.yaml %}
+{% endhighlight %}
+
+The example above will allow a user to specify a message to send back
+and the port where netcat will listen.
+The metadata on these parameters is available at runtime in the UI
+and through the API, and is used when populating a catalog.
+
+The example also shows how these values can be passed as environment variables to the launch command.
+The `$brooklyn:config(...)` function returns the config value supplied or default.
+For the type `port`, an attribute sensor is also created to report the *actual* port used after port inference,
+and so the `$brooklyn:attributeWhenReady(...)` function is used.
+(If `$brooklyn:config("netcat.port")` had been used, `4321+` would be passed as `NETCAT_PORT`.)
+
+This gives us quite a bit more power in writing our blueprint:
+
+* Multiple instances of the server can be launched simultaneously on the same host, 
+  as the `4321+` syntax enables Brooklyn to assign them different ports
+* If this type is added to the catalog, a user can configure the message and the port;
+  we'll show this in the next section
+
+
+#### Using the Catalog and Clustering
+
+The *Catalog* tab allows you to add blueprints which you can refer to in other blueprints.
+In that tab, click *+* then *YAML*, and enter the following:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-catalog.bom %}
+{% endhighlight %}
+
+This is the same example as in the previous section, wrapped according to the catalog YAML requirements,
+with one new block added defining an enricher. An enricher creates a new sensor from other values;
+in this case it will create a `main.uri` sensor by populating a `printf`-style string `"http://%s:%s"`
+with the sensor values.
+
+With this added to the catalog, we can reference the type `netcat-example` when we deploy an application.
+Return to the *Home* or *Applications* tab, click *+*, and submit this YAML blueprint:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-reference.yaml %}
+{% endhighlight %}
+
+This extends the previous blueprint which we registered in the catalog,
+meaning that we don't need to include it each time.
+Here, we've elected to supply our own message, but we'll use the default port.
+More importantly, we can package it for others to consume -- or take items others have built.
+
+We can go further and use this to deploy a cluster,
+this time giving a custom port as well as a custom message: 
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-cluster.yaml %}
+{% endhighlight %}
+
+In either of the above examples, if you explore the tree in the *Applications* view
+and look at the *Summary* tab of any of the server instances, you'll now see the URL where netcat is running.
+But remember, netcat will stop after one run, so you'll only be able to use each link once
+before you have to restart it.  You can also run `restart` on the cluster,
+and if you haven't yet experimented with `resize` on the cluster you might want to do that.
+
+
+#### Determining Successful Launch
+
+One requirement of the launch script is that it store the process ID (PID) in the file
 pointed to by `$PID_FILE`, hence the second line of the script.
-This is because Brooklyn wants to monitor the services under management. 
-(There are other options; you can set `checkRunning.command` and `stop.command` instead,
-as documented on the Javadoc of the `VanillaSoftwareProcess` class,
-and those scripts will be used instead of checking and stopping the process whose PID is in `$PID_FILE`.)
+This is because Brooklyn wants to monitor the services under management.
+You'll observe this if you connect to one of the netcat services,
+as the process exits afterwards and Brooklyn sets the entity to an `ON_FIRE` state.
+(You can also test this with a `killall nc` before connecting
+or issueing a `stop` command on the server -- but not on the example,
+as stopping an application root causes it to be removed altogether!) 
+
+There are other options for determining launch: you can set `checkRunning.command` and `stop.command` instead,
+as documented on the javadoc and config keys of the {% include java_link.html class_name="VanillaSoftwareProcess" package_path="org/apache/brooklyn/entity/software/base" project_subpath="software/base" %} class,
+and those scripts will be used instead of checking and stopping the process whose PID is in `$PID_FILE`.
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-more-commands.yaml %}
+{% endhighlight %}
 
 And indeed, once you've run one `telnet` to the server, you'll see that the 
-service has gone "on fire" in Brooklyn -- because the `nc` process has stopped. 
+service has gone "on fire" in Brooklyn -- because the `nc` process stops after one run. 
+
+
+#### Attaching Policies
+
 Besides detecting this failure, Brooklyn policies can be added to the YAML to take appropriate 
-action. A simple recovery here might just be to restart the process:
+action. A simple recovery here might just to automatically restart the process:
 
 {% highlight yaml %}
 {% readj example_yaml/vanilla-bash-netcat-restarter.yaml %}
@@ -67,6 +191,9 @@
 Running with this blueprint, you'll see that the service shows as on fire for 15s after a `telnet`,
 before the policy restarts it. 
 
+
+### Sensors and Effectors
+
 For an even more interesting way to test it, look at the blueprint defining
 [a netcat server and client](example_yaml/vanilla-bash-netcat-w-client.yaml).
 This uses `initializers` to define an effector to `sayHiNetcat` on the `Simple Pinger` client,
@@ -100,8 +227,11 @@
           period: 1s
           command: tail -1 server-input
 
-This is still a simple example, but worth going through carefully.
-It shows many of the building blocks used in real-world blueprints,
+
+#### Summary
+
+These examples are relatively simple example, but they
+illustrate many of the building blocks used in real-world blueprints,
 and how they can often be easily described and combined in Brooklyn YAML blueprints.
 Next, if you need to drive off-piste, or you want to write tests against these blueprints,
 have a look at, for example, `VanillaBashNetcatYamlTest.java` in the Brooklyn codebase,
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-catalog.bom b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-catalog.bom
new file mode 100644
index 0000000..7551818
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-catalog.bom
@@ -0,0 +1,35 @@
+brooklyn.catalog:
+  id: netcat-example
+  version: "1.0"
+  item:
+    type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+    name: Simple Netcat Server
+
+    launch.command: |
+      echo $MESSAGE | nc -l $NETCAT_PORT &
+      echo $! > $PID_FILE
+        
+    env:
+      MESSAGE: $brooklyn:config("message")
+      NETCAT_PORT: $brooklyn:attributeWhenReady("netcat.port")
+      
+    brooklyn.parameters:
+    - name: message
+      description: a message to send to the caller
+      default: hello
+    - name: netcat.port
+      type: port
+      description: the port netcat should run on
+      default: 4321+
+
+    brooklyn.enrichers:
+    - type: org.apache.brooklyn.enricher.stock.Transformer
+      brooklyn.config:
+        uniqueTag: main-uri-generator
+        enricher.sourceSensor: $brooklyn:sensor("host.address")
+        enricher.targetSensor: $brooklyn:sensor("main.uri")
+        enricher.targetValue:
+          $brooklyn:formatString:
+          - "http://%s:%s/"
+          - $brooklyn:attributeWhenReady("host.address")
+          - $brooklyn:attributeWhenReady("netcat.port")
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-cluster.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-cluster.yaml
new file mode 100644
index 0000000..70b69af
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-cluster.yaml
@@ -0,0 +1,11 @@
+name: Netcat Cluster Example
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.group.DynamicCluster
+  memberSpec:
+    $brooklyn:entitySpec:
+      type: netcat-example
+      message: hello from cluster member
+      netcat.port: 8000+
+  initialSize: 3
+  restartMode: parallel
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-more-commands.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-more-commands.yaml
new file mode 100644
index 0000000..f4e894f
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-more-commands.yaml
@@ -0,0 +1,16 @@
+name: Netcat Example with Explicit Check and Stop Commands
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+  name: Simple Netcat Server
+  launch.command: |
+    echo hello | nc -l 4321 &
+    echo $! > $PID_FILE
+
+  # The following overrides demonstrate the use of a custom shell environment as well as
+  # check-running and stop commands. These are optional; default behavior will "do the
+  # right thing" with the pid file automatically.
+
+  env:                  { CHECK_MARKER: "checkRunning", STOP_MARKER: "stop" }
+  checkRunning.command: echo $CHECK_MARKER >> DATE && test -f "$PID_FILE" && ps -p `cat $PID_FILE` >/dev/null
+  stop.command:         echo $STOP_MARKER  >> DATE && test -f "$PID_FILE" && { kill -9 `cat $PID_FILE`; rm /tmp/vanilla.pid; }
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port-parameter.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port-parameter.yaml
new file mode 100644
index 0000000..90f83b4
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port-parameter.yaml
@@ -0,0 +1,21 @@
+name: Netcat Example with Parameter
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+  name: Simple Netcat Server
+  launch.command: |
+    echo $MESSAGE | nc -l $NETCAT_PORT &
+    echo $! > $PID_FILE
+    
+  env:
+    MESSAGE: $brooklyn:config("message")
+    NETCAT_PORT: $brooklyn:attributeWhenReady("netcat.port")
+  
+  brooklyn.parameters:
+  - name: message
+    description: a message to send to the caller
+    default: hello
+  - name: netcat.port
+    type: port
+    description: the port netcat should run on
+    default: 4321+
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port.yaml
new file mode 100644
index 0000000..3ec0212
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port.yaml
@@ -0,0 +1,13 @@
+name: Netcat Example with Port Opened
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+  name: Simple Netcat Server
+  launch.command: |
+    echo hello | nc -l 4321 &
+    echo $! > $PID_FILE
+    
+  brooklyn.config:
+    # matching the regex `.*\.port` will cause the port to be opened
+    # if in a cloud where configurable security groups are available
+    netcat.port: 4321
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-reference.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-reference.yaml
new file mode 100644
index 0000000..0f10c55
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-reference.yaml
@@ -0,0 +1,5 @@
+name: Netcat Type Reference Example
+location: localhost
+services:
+- type: netcat-example
+  message: hello from netcat using a registered type
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml
index adaa933..47e54ab 100644
--- a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml
@@ -1,4 +1,4 @@
-name: Simple Netcat Example with Restarter Policy
+name: Netcat Example with Restarter Policy
 location: localhost
 services:
 - type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml
index 8290b79..78ed99e 100644
--- a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml
@@ -1,4 +1,4 @@
-name: Simple Netcat with Client
+name: Netcat Example with Client
 
 location: localhost
 
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml
index 3023038..df616af 100644
--- a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml
@@ -6,13 +6,3 @@
   launch.command: |
     echo hello | nc -l 4321 &
     echo $! > $PID_FILE
-
-  # The following overrides demonstrate the use of a custom shell environment as well as
-  # check-running and stop commands. These are optional; default behavior will "do the
-  # right thing" with the pid file automatically.
-
-  env:                  { CHECK_MARKER: "checkRunning", STOP_MARKER: "stop" }
-  checkRunning.command: echo $CHECK_MARKER >> DATE && test -f "$PID_FILE" && ps -p `cat $PID_FILE` >/dev/null
-  stop.command:         echo $STOP_MARKER  >> DATE && test -f "$PID_FILE" && { kill -9 `cat $PID_FILE`; rm /tmp/vanilla.pid; }
-
-# can also define download.url, in which case the launch command defaults to ./start.sh in that (archive) file
diff --git a/brooklyn-docs/guide/yaml/yaml-reference.md b/brooklyn-docs/guide/yaml/yaml-reference.md
index 36656bb..7a628a0 100644
--- a/brooklyn-docs/guide/yaml/yaml-reference.md
+++ b/brooklyn-docs/guide/yaml/yaml-reference.md
@@ -67,22 +67,32 @@
   * `description`: short text describing the parameter behaviour/usage, presented
     to the user
   * `type`: the type of the parameter, one of `string`, `integer`, `long`, `float`,
-    `double`, `timestamp`, `port`, a fully qualified Java type name. Default is `string`.
-  * `default`: a default value, converted to the type above
-  * `constraints`: a list of constraints the parameter should meet, currently
-    `required` is supported
+    `double`, `timestamp`, `duration`, `port`, or a fully qualified Java type name;
+    the default is `string`;
+    obvious coercion is supported so 
+    `timestamp` accepts most common ISO date formats, `duration` accepts `5m`, and port accepts `8080+`
+  * `default`: a default value; this will be coerced to the declared `type`
+  * `constraints`: a list of constraints the parameter should meet;
+    currently `required` is supported, with the default being not required
 
-  A shorthand notation is also supported where the name of the parameter is directly
-  passed as an item in the list. For example:
+  A shorthand notation is also supported where just the name of the parameter is supplied
+  as an item in the list, with the other values being unset or the default.
+  See `displayName` in the following example for an illustration of this:
 
 ~~~ yaml
 brooklyn.parameters:
-- displayName
-- name: user.name
-  constraints:
-  - required
+# user.age parameter is required, and fully specified
 - name: user.age
   type: integer
+  label: Age
+  description: the age of the user
+  constraints:
+  - required
+# user.name is optional, and has a default
+- name: user.name
+  default: You
+# shorthand notation: displayName will be an optional config of type string with no default
+- displayName
 ~~~
 
 Entities, policies, and initializers may accept additional key-value pairs,
diff --git a/brooklyn-docs/style/deps/glossarizer.js b/brooklyn-docs/style/deps/glossarizer.js
new file mode 100644
index 0000000..cdc5dbc
--- /dev/null
+++ b/brooklyn-docs/style/deps/glossarizer.js
@@ -0,0 +1,379 @@
+/**
+ * Plugin Name: Glossarizer
+ * Author : Vinay @Pebbleroad
+ * Date: 02/04/2013
+ * Description: Takes glossary terms from a JSON object -> Searches for terms in your html -> Wraps a abbr tag around the matched word
+ * 1. Fixed IE8 bug where whitespace get removed - Had to change `abbr` tag to a block element `div`
+ */
+
+;(function($){
+
+	/**
+	 * Defaults
+	 */
+	
+	var pluginName = 'glossarizer',
+		defaults = {
+			sourceURL     : '', /* URL of the JSON file with format {"term": "", "description": ""} */	
+			replaceTag    : 'abbr', /* Matching words will be wrapped with abbr tags by default */
+			lookupTagName : 'p, ul, a, div', /* Lookup in either paragraphs or lists. Do not replace in headings */
+			callback      : null, /* Callback once all tags are replaced: Call or tooltip or anything you like */
+			replaceOnce   : false /* Replace only once in a TextNode */,
+			replaceClass  : 'glossarizer_replaced',
+			caseSensitive : false
+		}
+
+	/**
+	 * Constructor
+	 */
+	
+	function Glossarizer(el, options){
+
+		var base = this
+
+		base.el = el;
+
+		/* Element */
+		base.$el = $(el)
+
+		/* Extend options */
+
+		base.options = $.extend({}, defaults, options)
+
+		/* Terms */
+		
+		base.terms = [];
+
+		/* Excludes array */
+
+		base.excludes = [];
+
+		/* Replaced words array */
+
+		base.replaced = [];
+		
+
+		/* Regex Tags */
+		
+		base.regexOption = (base.options.caseSensitive? '': 'i') + (base.options.replaceOnce? '': 'g');
+
+		
+		/* Fetch glossary JSON */
+
+		$.getJSON(this.options.sourceURL).then(function(data){
+
+			base.glossary = data;
+			
+			if(!base.glossary.length || base.glossary.length == 0) return;			
+			
+			/**
+			 * Get all terms
+			 */
+			
+			for(var i =0; i< base.glossary.length; i++){
+				
+				var terms = base.glossary[i].term.split(',');
+
+				for(var j = 0; j < terms.length; j++){
+
+					/* Trim */
+
+					var trimmed = terms[j].replace(/^\s+|\s+$/g, ''),
+						isExclusion = trimmed.indexOf('!');
+					
+					if(isExclusion == -1 || isExclusion != 0){
+
+						/* Glossary terms array */
+						
+						base.terms.push(trimmed)
+
+					}else{
+
+						/* Excluded terms array */
+						
+						base.excludes.push(trimmed.substr(1));
+					}
+				}
+				
+				
+			}
+			
+
+			/**
+			 * Wrap terms
+			 */
+			
+			base.wrapTerms();
+
+
+		})
+
+		
+
+	}
+
+	/**
+	 * Prototypes
+	 */
+	Glossarizer.prototype = {		
+
+		getDescription: function(term){			
+
+			var regex = new RegExp('(\,|\s*)'+this.clean(term)+'\\s*|\\,$', 'i');
+
+			/**
+			 * Matches
+			 * 1. Starts with \s* (zero or more spaces)			 
+			 * 2. Ends with zero or more spaces
+			 * 3. Ends with comma
+			 */
+			
+			for(var i =0; i< this.glossary.length; i++){				
+
+				if(this.glossary[i].term.match(regex)){										
+					return this.glossary[i].description.replace(/\"/gi, '&quot;')
+				}				
+			}				
+
+		},
+		clean: function(text){
+
+			var reEscape = new RegExp('(\\' + ['/', '.', '*', '+', '?', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g')
+			
+			return text.replace(reEscape, '\\$1')
+
+		},
+		
+		/**
+		 * Wraps the matched terms by calling traverser     
+		 */
+		wrapTerms: function(){
+
+			this.cleanedTerms = this.clean(this.terms.join('|'))
+			this.excludedTerms = this.clean(this.excludes.join('|'))
+			
+			var nodes = this.el.querySelectorAll(this.options.lookupTagName)					
+
+			for(var i =0; i < nodes.length; i++){
+				this.traverser(nodes[i])
+			}      
+
+			/**
+			 * Callback
+			 */
+			
+			if(this.options.callback) this.options.callback.call(this.$el)
+
+		},
+
+		/**
+		 * Traverses through nodes to find the matching terms in TEXTNODES
+		 */
+
+		traverser: function(node){      
+			
+			var next,
+				base = this;
+
+			if (node.nodeType === 1) {
+
+				/*
+				 Element Node
+				 */
+				
+				if (node = node.firstChild) {
+						do {
+							// Recursively call traverseChildNodes
+							// on each child node
+							next = node.nextSibling
+
+							/**
+							 * Check if the node is not glossarized
+							 */
+
+							if(	node.className != this.options.replaceClass)
+							{
+								
+								this.traverser(node)
+
+							}
+
+						} while(node = next)
+				}
+
+			} else if (node.nodeType === 3) {
+
+				/*
+				 Text Node
+				 */
+
+				var temp = document.createElement('div'),
+					data = node.data;
+
+				var re = new RegExp('(?:^|\\b)('+this.cleanedTerms+ ')(?!\\w)', base.regexOption),
+					reEx = new RegExp('(?:^|\\b)('+this.excludedTerms+ ')(?!\\w)', base.regexOption);
+				
+				
+				if(re.test(data)){      
+					
+					var excl = reEx.exec(data);    
+					
+					data = data.replace(re,function(match, item , offset, string){
+						
+
+						if(base.options.replaceOnce && inArrayIn(match, base.replaced) >= 0){
+
+							return match;
+						}
+						
+						base.replaced.push(match);
+						
+						var ir = new RegExp('(?:^|\\b)'+base.clean(match)+'(?!\\w)'),
+							result = ir.exec(data)
+						
+						
+						if(result){
+
+							if(excl && base.excludes.length){
+								
+								var id = offset,
+									exid = excl.index,
+									exl = excl.index + excl[0].length;
+								
+								if(exid <= id && id <= exl){
+
+									return match;
+									
+								}else{
+
+									return '<'+base.options.replaceTag+' class="'+base.options.replaceClass+'" title="'+base.getDescription(match)+'">'+ match + '</'+base.options.replaceTag+'>'
+
+								}
+							}
+							else{
+
+								return '<'+base.options.replaceTag+' class="'+base.options.replaceClass+'" title="'+base.getDescription(match)+'">'+ match + '</'+base.options.replaceTag+'>'
+							}
+						}
+						
+
+					});
+
+					/**
+					 * Only replace when a match is found					 
+					 * Resorting to jQuery html() because of IE8 whitespace issue.
+					 * IE 8 removes leading whitespace from Text Nodes. Hence innerhtml doesnt work.
+					 * 
+					 */
+					
+					$(temp).html(data)
+
+					
+				
+					while (temp.firstChild) {          
+						node.parentNode.insertBefore(temp.firstChild, node)
+					}
+
+					node.parentNode.removeChild(node)
+
+				}
+
+			}
+
+		},
+
+	};
+
+
+	/**
+	 * Public Methods
+	 */
+	
+	var methods = {
+
+		destroy: function(){			
+
+			this.$el.removeData('plugin_' + pluginName);
+
+			/* Remove abbr tag */
+			this.$el.find('.' + this.options.replaceClass).each(function(){
+
+				var $this = $(this),
+					text = $this.text();
+
+
+				$this.replaceWith(text)
+
+			})
+			
+		}
+	}
+
+
+	/**
+	 * Extend Prototype
+	 */
+	
+	Glossarizer.prototype = $.extend({}, Glossarizer.prototype, methods)
+
+	/**
+	 * Plugin
+	 * @param  {[type]} options   
+	 */
+	$.fn[pluginName] =function(options){
+
+		return this.each(function(){
+
+
+			var $this = $(this),
+				glossarizer = $this.data('plugin_' + pluginName);
+
+			/*
+			Check if its a method
+			 */
+			
+			if(typeof options == "string" && glossarizer  && methods.hasOwnProperty(options) ){
+
+				glossarizer[options].apply(glossarizer)
+
+			}else{
+
+				/* Destroy if exists */
+
+				if(glossarizer) glossarizer['destroy'].apply(glossarizer);
+
+
+				/* Initialize */
+			
+				$.data(this, 'plugin_' + pluginName, new Glossarizer(this, options))
+			}
+		});
+
+	}
+
+
+	/**
+	 * In Array
+	 */
+	
+	function inArrayIn(elem, arr, i){            
+        
+        if (typeof elem !== 'string'){
+			return $.inArray.apply(this, arguments);
+        }
+
+        if (arr){
+            var len = arr.length;
+                i = i ? (i < 0 ? Math.max(0, len + i) : i) : 0;
+            elem = elem.toLowerCase();
+            for (; i < len; i++){
+                if (i in arr && arr[i].toLowerCase() == elem){
+                    return i;
+                }
+            }
+        }            
+        return -1;
+    }
+
+
+})(jQuery);
\ No newline at end of file
diff --git a/brooklyn-docs/style/deps/tooltip.css b/brooklyn-docs/style/deps/tooltip.css
new file mode 100644
index 0000000..fc5c3a3
--- /dev/null
+++ b/brooklyn-docs/style/deps/tooltip.css
@@ -0,0 +1,64 @@
+/* fix for double underline on firefox */
+abbr[title], abbr[data-original-title]{
+	border-bottom: none;	
+}
+
+ #tooltip
+{
+    text-align: center;
+    color: #fff;
+    background: #000;
+    background: rgba(0,0,0,0.8);
+    position: absolute;
+    z-index: 100;
+    padding: 15px;
+    border-radius: 5px;
+    box-sizing: border-box;    
+}
+
+    @media only screen and (max-width: 800px){
+        #tooltip:before{
+            content: 'x';
+            position: absolute;
+            color: #fff;
+            right: 8px;
+            top: 3px;
+            font-size: 12px;
+        }
+    }    
+ 
+    #tooltip:after /* triangle decoration */
+    {
+        width: 0;
+        height: 0;
+        border-left: 10px solid transparent;
+        border-right: 10px solid transparent;
+        border-top: 10px solid rgba(0,0,0,0.8);
+        content: '';
+        position: absolute;
+        left: 50%;
+        bottom: -10px;
+        margin-left: -10px;
+    }
+ 
+        #tooltip.top:after
+        {
+            border-top-color: transparent;
+            border-bottom: 10px solid #111;
+            top: -20px;                         
+            bottom: auto;
+        }
+
+ 
+        #tooltip.left:after
+        {
+            left: 10px;
+            margin: 0;
+        }
+ 
+        #tooltip.right:after
+        {
+            right: 10px;
+            left: auto;
+            margin: 0;
+        }
\ No newline at end of file
diff --git a/brooklyn-docs/style/deps/tooltip.js b/brooklyn-docs/style/deps/tooltip.js
new file mode 100644
index 0000000..d1e8c54
--- /dev/null
+++ b/brooklyn-docs/style/deps/tooltip.js
@@ -0,0 +1,86 @@
+// 
+// Author : http://osvaldas.info/elegant-css-and-jquery-tooltip-responsive-mobile-friendly
+// 
+(function($, window, undefined){
+
+    function ToolTip (){
+
+        var targets = $( '.glossarizer_replaced' ),
+            target  = false,
+            tooltip = false,
+            title   = false;
+     
+        targets.bind( 'mouseenter', function()
+        {
+            target  = $( this );
+            tip     = target.attr( 'title' );
+            tooltip = $( '<div id="tooltip"></div>' );
+     
+            if( !tip || tip == '' )
+                return false;
+     
+            target.removeAttr( 'title' );
+            tooltip.css( 'opacity', 0 )
+                   .html( tip )
+                   .appendTo( 'body' );
+     
+            var init_tooltip = function()
+            {
+                if( $( window ).width() < tooltip.outerWidth() * 1.5 )
+                    tooltip.css( 'max-width', $( window ).width() / 2 );
+                else
+                    tooltip.css( 'max-width', 340 );
+     
+                var pos_left = target.offset().left + ( target.outerWidth() / 2 ) - ( tooltip.outerWidth() / 2 ),
+                    pos_top  = target.offset().top - tooltip.outerHeight() - 20;
+     
+                if( pos_left < 0 )
+                {
+                    pos_left = target.offset().left + target.outerWidth() / 2 - 20;
+                    tooltip.addClass( 'left' );
+                }
+                else
+                    tooltip.removeClass( 'left' );
+     
+                if( pos_left + tooltip.outerWidth() > $( window ).width() )
+                {
+                    pos_left = target.offset().left - tooltip.outerWidth() + target.outerWidth() / 2 + 20;
+                    tooltip.addClass( 'right' );
+                }
+                else
+                    tooltip.removeClass( 'right' );
+     
+                if( pos_top < 0 )
+                {
+                    var pos_top  = target.offset().top + target.outerHeight();
+                    tooltip.addClass( 'top' );
+                }
+                else
+                    tooltip.removeClass( 'top' );
+     
+                tooltip.css( { left: pos_left, top: pos_top } )
+                       .animate( { top: '+=10', opacity: 1 }, 50 );
+            };
+     
+            init_tooltip();
+            $( window ).resize( init_tooltip );
+     
+            var remove_tooltip = function()
+            {
+                tooltip.animate( { top: '-=10', opacity: 0 }, 50, function()
+                {
+                    $( this ).remove();
+                });
+     
+                target.attr( 'title', tip );
+            };
+     
+            target.bind( 'mouseleave', remove_tooltip );
+            tooltip.bind( 'click', remove_tooltip );
+        });
+
+    }
+
+    return window.tooltip = ToolTip;
+
+})(jQuery, window)
\ No newline at end of file
diff --git a/brooklyn-docs/website/developers/committers/release-process/make-release-artifacts.md b/brooklyn-docs/website/developers/committers/release-process/make-release-artifacts.md
index f72e0c3..8b14c7d 100644
--- a/brooklyn-docs/website/developers/committers/release-process/make-release-artifacts.md
+++ b/brooklyn-docs/website/developers/committers/release-process/make-release-artifacts.md
@@ -4,12 +4,12 @@
 navgroup: developers
 ---
 
-A release script is provided in `release/make-release-artifacts.sh`. This script will prepare all the release artifacts.
+A release script is provided in `brooklyn-dist/release/make-release-artifacts.sh`. This script will prepare all the release artifacts.
 It is written to account for several Apache requirements, so you are strongly advised to use it rather than "rolling your own".
 
 The release script will:
 
-- **Create source code and binary distribution artifacts** and place them in a temporary staging directory on your workstation, usually `releases/`.
+- **Create source code and binary distribution artifacts** and place them in a temporary staging directory on your workstation, usually `brooklyn-dist/release/tmp/`.
 - **Create Maven artifacts and upload them to a staging repository** located on the Apache Nexus server.
 
 The script has a single required parameter `-r` which is given the release candidate number - so `-r1` will create
@@ -25,7 +25,7 @@
 To run the script:
 
 {% highlight bash %}
-./release/make-release-artifacts.sh -r$RC_NUMBER
+./brooklyn-dist/release/make-release-artifacts.sh -r$RC_NUMBER
 {% endhighlight %}
 
 It will show you the release information it has deduced, and ask yes-or-no if it can proceed. Please note that the
diff --git a/brooklyn-docs/website/developers/committers/release-process/publish.md b/brooklyn-docs/website/developers/committers/release-process/publish.md
index b10d262..436dbc4 100644
--- a/brooklyn-docs/website/developers/committers/release-process/publish.md
+++ b/brooklyn-docs/website/developers/committers/release-process/publish.md
@@ -83,7 +83,7 @@
 ------------------
 
 *Instructions on uploading to the website are beyond the scope of these instructions. Refer to the 
-[appropriate instructions](https://github.com/apache/incubator-brooklyn/tree/master/docs).*
+[appropriate instructions](https://github.com/apache/incubator-brooklyn/tree/master/brooklyn-docs).*
 
 ### Publish documentation for the new release
 
@@ -105,7 +105,7 @@
 Generate the permalink docs for the release:
 
 {% highlight bash %}
-cd docs
+cd brooklyn-docs
 ./_build/build.sh guide-version --install
 {% endhighlight %}
 
@@ -135,11 +135,11 @@
 git checkout master
 {% endhighlight %}
 
-1. Edit the file `docs/_config.yml` - change `brooklyn-stable-version` to be the newly-release version, and
+1. Edit the file `brooklyn-docs/_config.yml` - change `brooklyn-stable-version` to be the newly-release version, and
    `brooklyn-version` to be the current SNAPSHOT version on the master branch.
-2. Edit the file `docs/website/download/verify.md` to add links to the MD5/SHA1/SHA256 hashes and PGP signatures for the
+2. Edit the file `brooklyn-docs/website/download/verify.md` to add links to the MD5/SHA1/SHA256 hashes and PGP signatures for the
    new version.
-3. Edit the file `docs/website/meta/versions.md` to add the new version.
+3. Edit the file `brooklyn-docs/website/meta/versions.md` to add the new version.
 4. Build the updated site with `./_build/build.sh website-root --install`.
 5. Publish to the public website.
 6. Commit your changes to master, e.g. with a message like "Update latest docs to 0.8.0-incubating"
diff --git a/brooklyn-docs/website/developers/committers/release-process/release-version.md b/brooklyn-docs/website/developers/committers/release-process/release-version.md
index eea6943..a22314b 100644
--- a/brooklyn-docs/website/developers/committers/release-process/release-version.md
+++ b/brooklyn-docs/website/developers/committers/release-process/release-version.md
@@ -32,10 +32,10 @@
 git push -u apache $VERSION_NAME
 {% endhighlight %}
 
-Now change the version numbers in this branch throughout the project using the script `release/change-version.sh` and commit it:
+Now change the version numbers in this branch throughout the project using the script `brooklyn-dist/release/change-version.sh` and commit it:
 
 {% highlight bash %}
-./release/change-version.sh BROOKLYN $OLD_MASTER_VERSION $VERSION_NAME
+./brooklyn-dist/release/change-version.sh BROOKLYN $OLD_MASTER_VERSION $VERSION_NAME
 git add .
 # Now inspect the staged changes and ensure there are no surprises
 git commit -m "Change version to $VERSION_NAME"
@@ -56,12 +56,12 @@
 
 {% highlight bash %}
 git checkout master
-./release/change-version.sh BROOKLYN $OLD_MASTER_VERSION $NEW_MASTER_VERSION
+./brooklyn-dist/release/change-version.sh BROOKLYN $OLD_MASTER_VERSION $NEW_MASTER_VERSION
 git add .
 # Now inspect the staged changes and ensure there are no surprises
 {% endhighlight %}
 
-Open `docs/guide/misc/release-notes.md` and `docs/website/meta/versions.md` in your favourite editor and amend.
+Open `brooklyn-docs/guide/misc/release-notes.md` and `brooklyn-docs/website/meta/versions.md` in your favourite editor and amend.
 For release notes this means bumping the reference to the previous version in the "Backwards Compatibility" section
 and putting some placeholder text elsewhere.
 
diff --git a/brooklyn-docs/website/developers/committers/release-process/vote.md b/brooklyn-docs/website/developers/committers/release-process/vote.md
index d3f6930..e459bdd 100644
--- a/brooklyn-docs/website/developers/committers/release-process/vote.md
+++ b/brooklyn-docs/website/developers/committers/release-process/vote.md
@@ -7,10 +7,10 @@
 Start the vote
 --------------
 
-A script to generate the voting email can be found in `release/print-vote-email.sh`,
+A script to generate the voting email can be found in `brooklyn-dist/release/print-vote-email.sh`,
 taking a single argument being the staging repo link. For example:
 
-    release/print-vote-email.sh orgapachebrooklyn-1234 | pbcopy 
+    brooklyn-dist/release/print-vote-email.sh orgapachebrooklyn-1234 | pbcopy
 
 You should move the subject and put your name at the end, and simply eyeball the rest. This should be sent to **dev@brooklyn.incubator.apache.org**.
 
diff --git a/brooklyn-library/LICENSE b/brooklyn-library/LICENSE
index ca60394..3d8f4e7 100644
--- a/brooklyn-library/LICENSE
+++ b/brooklyn-library/LICENSE
@@ -282,6 +282,13 @@
   Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
   Copyright (c) Vitaly Puzrin (2011-2015)
 
+This project includes the software: marked.js
+  Available at: https://github.com/chjj/marked
+  Developed by: Christopher Jeffrey (https://github.com/chjj)
+  Version used: 0.3.1
+  Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
+  Copyright (c) Christopher Jeffrey (2011-2014)
+
 This project includes the software: moment.js
   Available at: http://momentjs.com
   Developed by: Tim Wood (http://momentjs.com)
@@ -323,17 +330,10 @@
       Arpad Borsos (2012)
     Used under the BSD 2-Clause license.
 
-This project includes the software: Swagger JS
-  Available at: https://github.com/wordnik/swagger-js
-  Inclusive of: swagger.js
-  Version used: 1.0.1
-  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
-  Copyright (c) SmartBear Software (2011-2015)
-
 This project includes the software: Swagger UI
-  Available at: https://github.com/wordnik/swagger-ui
-  Inclusive of: swagger-ui.js
-  Version used: 1.0.1
+  Available at: https://github.com/swagger-api/swagger-ui
+  Inclusive of: swagger*.{js,css,html}
+  Version used: 2.1.4
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
   Copyright (c) SmartBear Software (2011-2015)
 
diff --git a/brooklyn-library/pom.xml b/brooklyn-library/pom.xml
index 71b87f4..2b6ba8d 100644
--- a/brooklyn-library/pom.xml
+++ b/brooklyn-library/pom.xml
@@ -86,7 +86,7 @@
         <activemq.version>5.10.0</activemq.version>
         <rabbitmq-version>2.8.7</rabbitmq-version>
         <kafka.version>0.8.2.1</kafka.version>
-        <storm.version>0.8.2</storm.version>
+        <storm.version>0.9.0.1</storm.version>
         <redis.version>1.5.2</redis.version>
         
     </properties>
diff --git a/brooklyn-library/software/messaging/pom.xml b/brooklyn-library/software/messaging/pom.xml
index 83e7ef0..57b2ff0 100644
--- a/brooklyn-library/software/messaging/pom.xml
+++ b/brooklyn-library/software/messaging/pom.xml
@@ -181,7 +181,7 @@
         <!-- for storm -->
 		<dependency>
 			<groupId>storm</groupId>
-			<artifactId>storm</artifactId>
+			<artifactId>storm-core</artifactId>
 			<version>${storm.version}</version>
 			<!-- keep storm out of the jar-with-dependencies -->
             <scope>test</scope>
@@ -215,6 +215,10 @@
                     <groupId>commons-codec</groupId>
                     <artifactId>commons-codec</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>storm</groupId>
+                    <artifactId>carbonite</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
 
diff --git a/brooklyn-server/LICENSE b/brooklyn-server/LICENSE
index ca60394..3d8f4e7 100644
--- a/brooklyn-server/LICENSE
+++ b/brooklyn-server/LICENSE
@@ -282,6 +282,13 @@
   Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
   Copyright (c) Vitaly Puzrin (2011-2015)
 
+This project includes the software: marked.js
+  Available at: https://github.com/chjj/marked
+  Developed by: Christopher Jeffrey (https://github.com/chjj)
+  Version used: 0.3.1
+  Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
+  Copyright (c) Christopher Jeffrey (2011-2014)
+
 This project includes the software: moment.js
   Available at: http://momentjs.com
   Developed by: Tim Wood (http://momentjs.com)
@@ -323,17 +330,10 @@
       Arpad Borsos (2012)
     Used under the BSD 2-Clause license.
 
-This project includes the software: Swagger JS
-  Available at: https://github.com/wordnik/swagger-js
-  Inclusive of: swagger.js
-  Version used: 1.0.1
-  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
-  Copyright (c) SmartBear Software (2011-2015)
-
 This project includes the software: Swagger UI
-  Available at: https://github.com/wordnik/swagger-ui
-  Inclusive of: swagger-ui.js
-  Version used: 1.0.1
+  Available at: https://github.com/swagger-api/swagger-ui
+  Inclusive of: swagger*.{js,css,html}
+  Version used: 2.1.4
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
   Copyright (c) SmartBear Software (2011-2015)
 
diff --git a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
index 4e9dc62..fd7047e 100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
@@ -20,13 +20,23 @@
 
 import java.io.Serializable;
 
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.config.ConfigKey;
 
+/** A wrapper around a {@link ConfigKey} which will be added to an {@link Entity},
+ * providing additional information for rendering in a UI */
 public interface SpecParameter<T> extends Serializable {
     /** Short name, to be used in UI */
     String getLabel();
-    /** Visible by default in UI, not all inputs may be visible at once */
+    /** Whether visible by default in UI, not all inputs may be visible at once */
     boolean isPinned();
-    /** Type information for the input */
-    ConfigKey<T> getType();
+    /** All config key info for this spec parameter;
+     * this is the config key which is added to the defined type */
+    ConfigKey<T> getConfigKey();
+    /** An optional sensor which may also be added to the defined type */
+    @Nullable AttributeSensor<?> getSensor();
+
 }
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java
index fdf5807..869ebc0 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java
@@ -62,20 +62,20 @@
         SpecParameter<?> firstInput = inputs.get(0);
         assertEquals(firstInput.getLabel(), "simple");
         assertEquals(firstInput.isPinned(), true);
-        assertEquals(firstInput.getType().getName(), "simple");
-        assertEquals(firstInput.getType().getTypeToken(), TypeToken.of(String.class));
+        assertEquals(firstInput.getConfigKey().getName(), "simple");
+        assertEquals(firstInput.getConfigKey().getTypeToken(), TypeToken.of(String.class));
         
         SpecParameter<?> secondInput = inputs.get(1);
         assertEquals(secondInput.getLabel(), "explicit_name");
         assertEquals(secondInput.isPinned(), true);
-        assertEquals(secondInput.getType().getName(), "explicit_name");
-        assertEquals(secondInput.getType().getTypeToken(), TypeToken.of(String.class));
+        assertEquals(secondInput.getConfigKey().getName(), "explicit_name");
+        assertEquals(secondInput.getConfigKey().getTypeToken(), TypeToken.of(String.class));
         
         SpecParameter<?> thirdInput = inputs.get(2);
         assertEquals(thirdInput.getLabel(), "third_input");
         assertEquals(thirdInput.isPinned(), true);
-        assertEquals(thirdInput.getType().getName(), "third_input");
-        assertEquals(thirdInput.getType().getTypeToken(), TypeToken.of(Integer.class));
+        assertEquals(thirdInput.getConfigKey().getName(), "third_input");
+        assertEquals(thirdInput.getConfigKey().getTypeToken(), TypeToken.of(Integer.class));
     }
 
     @Test
@@ -99,8 +99,8 @@
         SpecParameter<?> firstInput = inputs.get(0);
         assertEquals(firstInput.getLabel(), "simple");
         assertTrue(firstInput.isPinned());
-        assertEquals(firstInput.getType().getName(), "simple");
-        assertEquals(firstInput.getType().getTypeToken().getRawType().getName(), OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY);
+        assertEquals(firstInput.getConfigKey().getName(), "simple");
+        assertEquals(firstInput.getConfigKey().getTypeToken().getRawType().getName(), OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY);
     }
 
     @Test
@@ -126,7 +126,7 @@
         SpecParameter<?> input = inputs.get(0);
         assertEquals(input.getLabel(), "more_config");
         assertFalse(input.isPinned());
-        assertEquals(input.getType().getName(), "more_config");
+        assertEquals(input.getConfigKey().getName(), "more_config");
     }
 
     private String add(String... def) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
index 2e59185..f158c2c 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
@@ -49,7 +49,6 @@
 
 public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializable {
     
-    @SuppressWarnings("unused")
     private static final Logger log = LoggerFactory.getLogger(BasicConfigKey.class);
     private static final long serialVersionUID = -1762014059150215376L;
     
@@ -119,6 +118,13 @@
         public BasicConfigKey<T> build() {
             return new BasicConfigKey<T>(this);
         }
+        
+        public String getName() {
+            return name;
+        }
+        public String getDescription() {
+            return description;
+        }
     }
     
     private String name;
@@ -165,7 +171,7 @@
         this.constraint = Predicates.alwaysTrue();
     }
 
-    protected BasicConfigKey(Builder<T> builder) {
+    public BasicConfigKey(Builder<T> builder) {
         this.name = checkNotNull(builder.name, "name");
         this.type = TypeTokens.getRawTypeIfRaw(checkNotNull(builder.type, "type"));
         this.typeToken = TypeTokens.getTypeTokenIfNotRaw(builder.type);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
index 1a273f6..c508349 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
@@ -28,10 +28,9 @@
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.objs.EntityAdjunct;
 import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.location.AbstractLocation;
 import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
-import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
+import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
index 63ea52d..9b10d1d 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
@@ -173,14 +173,26 @@
         return ConfigKeys.newConfigKey(paramType.getParameterClass(), paramType.getName(), paramType.getDescription(), paramType.getDefaultValue());
     }
 
-    /** returns an unsubmitted task which will invoke the given effector on the given entities;
-     * return type is Task<List<T>> (but haven't put in the blood sweat toil and tears to make the generics work) */
+    /** convenience for {@link #invocationParallel(Effector, Map, Iterable)} */
     public static TaskAdaptable<List<?>> invocation(Effector<?> eff, Map<?,?> params, Iterable<? extends Entity> entities) {
+        return invocationParallel(eff, params, entities);
+    }
+    
+    /** returns an unsubmitted task which will invoke the given effector on the given entities in parallel;
+     * return type is Task<List<T>> (but haven't put in the blood sweat toil and tears to make the generics work) */
+    public static TaskAdaptable<List<?>> invocationParallel(Effector<?> eff, Map<?,?> params, Iterable<? extends Entity> entities) {
         List<TaskAdaptable<?>> tasks = new ArrayList<TaskAdaptable<?>>();
         for (Entity e: entities) tasks.add(invocation(e, eff, params));
         return Tasks.parallel("invoking "+eff+" on "+tasks.size()+" node"+(Strings.s(tasks.size())), tasks.toArray(new TaskAdaptable[tasks.size()]));
     }
 
+    /** as {@link #invocationParallel(Effector, Map, Iterable)} but executing sequentially */
+    public static TaskAdaptable<List<?>> invocationSequential(Effector<?> eff, Map<?,?> params, Iterable<? extends Entity> entities) {
+        List<TaskAdaptable<?>> tasks = new ArrayList<TaskAdaptable<?>>();
+        for (Entity e: entities) tasks.add(invocation(e, eff, params));
+        return Tasks.sequential("invoking "+eff+" on "+tasks.size()+" node"+(Strings.s(tasks.size())), tasks.toArray(new TaskAdaptable[tasks.size()]));
+    }
+
     /** returns an unsubmitted task which will invoke the given effector on the given entities
      * (this form of method is a convenience for {@link #invocation(Effector, Map, Iterable)}) */
     public static TaskAdaptable<List<?>> invocation(Effector<?> eff, MutableMap<?, ?> params, Entity ...entities) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
index 6cb8bb4..1d4cfea 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
@@ -19,7 +19,6 @@
 
 package org.apache.brooklyn.core.objs;
 
-import java.util.concurrent.TimeUnit;
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.mgmt.ExecutionContext;
@@ -28,8 +27,8 @@
 import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.core.task.ValueResolver;
 import org.apache.brooklyn.util.guava.Maybe;
-import org.apache.brooklyn.util.time.Duration;
 
 public abstract class AbstractConfigurationSupportInternal implements BrooklynObjectInternal.ConfigurationSupportInternal {
 
@@ -63,7 +62,7 @@
         Object resolved = Tasks.resolving(unresolved)
                 .as(Object.class)
                 .defaultValue(marker)
-                .timeout(Duration.of(5, TimeUnit.MILLISECONDS))
+                .timeout(ValueResolver.REAL_REAL_QUICK_WAIT)
                 .context(getContext())
                 .swallowExceptions()
                 .get();
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
index dfbe1c1..2bc2346 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
@@ -36,12 +36,18 @@
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.objs.BrooklynType;
 import org.apache.brooklyn.api.objs.SpecParameter;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
 import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey.Builder;
+import org.apache.brooklyn.core.sensor.PortAttributeSensorAndConfigKey;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.StringPredicates;
+import org.apache.brooklyn.util.time.Duration;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
@@ -54,14 +60,26 @@
 public class BasicSpecParameter<T> implements SpecParameter<T>{
     private static final long serialVersionUID = -4728186276307619778L;
 
-    private String label;
-    private boolean pinned;
-    private ConfigKey<T> type;
+    private final String label;
+    
+    /** pinning may become a priority or other more expansive indicator */
+    @Beta
+    private final boolean pinned;
+    
+    private final ConfigKey<T> configKey;
+    private final AttributeSensor<?> sensor;
 
-    public BasicSpecParameter(String label, boolean pinned, ConfigKey<T> type) {
+    @Beta // TBD whether "pinned" stays
+    public BasicSpecParameter(String label, boolean pinned, ConfigKey<T> config) {
+        this(label, pinned, config, null);
+    }
+
+    @Beta // TBD whether "pinned" and "sensor" stay
+    public <SensorType> BasicSpecParameter(String label, boolean pinned, ConfigKey<T> config, AttributeSensor<SensorType> sensor) {
         this.label = label;
         this.pinned = pinned;
-        this.type = type;
+        this.configKey = config;
+        this.sensor = sensor;
     }
 
     @Override
@@ -73,15 +91,20 @@
     public boolean isPinned() {
         return pinned;
     }
+    
+    @Override
+    public ConfigKey<T> getConfigKey() {
+        return configKey;
+    }
 
     @Override
-    public ConfigKey<T> getType() {
-        return type;
+    public AttributeSensor<?> getSensor() {
+        return sensor;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(label, pinned, type);
+        return Objects.hashCode(label, pinned, configKey);
     }
 
     @Override
@@ -95,7 +118,7 @@
         BasicSpecParameter<?> other = (BasicSpecParameter<?>) obj;
         return Objects.equal(label,  other.label) &&
                 pinned == other.pinned &&
-                Objects.equal(type, other.type);
+                Objects.equal(configKey, other.configKey);
     }
 
     @Override
@@ -103,7 +126,7 @@
         return Objects.toStringHelper(this)
                 .add("label", label)
                 .add("pinned", pinned)
-                .add("type", type)
+                .add("type", configKey)
                 .toString();
     }
 
@@ -154,6 +177,7 @@
                 .put("long", Long.class)
                 .put("float", Float.class)
                 .put("double", Double.class)
+                .put("duration", Duration.class)
                 .put("timestamp", Date.class)
                 .put("port", PortRange.class)
                 .build();
@@ -191,13 +215,23 @@
                 throw new IllegalArgumentException("'name' value missing from input definition " + obj + " but is required. Check for typos.");
             }
 
-            ConfigKey inputType = BasicConfigKey.builder(inferType(type, loader))
-                    .name(name)
-                    .description(description)
-                    .defaultValue(defaultValue)
-                    .constraint(constraints)
-                    .build();
-            return new BasicSpecParameter(Objects.firstNonNull(label, name), true, inputType);
+            ConfigKey configType;
+            AttributeSensor sensorType = null;
+            
+            TypeToken typeToken = inferType(type, loader);
+            Builder builder = BasicConfigKey.builder(typeToken)
+                .name(name)
+                .description(description)
+                .defaultValue(defaultValue)
+                .constraint(constraints);
+            
+            if (PortRange.class.equals(typeToken.getRawType())) {
+                sensorType = new PortAttributeSensorAndConfigKey(builder);
+                configType = ((HasConfigKey)sensorType).getConfigKey();
+            } else {
+                configType = builder.build();
+            }
+            return new BasicSpecParameter(Objects.firstNonNull(label, name), true, configType, sensorType);
         }
 
         @SuppressWarnings({ "rawtypes" })
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
index 2da7463..89c4e32 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
@@ -69,6 +69,9 @@
         /**
          * Returns the uncoerced value for this config key, if available, not taking any default.
          * If there is no local value and there is an explicit inherited value, will return the inherited.
+         * Returns {@link Maybe#absent()} if the key is not explicitly set on this object or an ancestor.
+         * <p>
+         * See also {@link #getLocalRaw(ConfigKey).
          */
         @Beta
         Maybe<Object> getRaw(ConfigKey<?> key);
@@ -82,6 +85,9 @@
         /**
          * Returns the uncoerced value for this config key, if available,
          * not following any inheritance chains and not taking any default.
+         * Returns {@link Maybe#absent()} if the key is not explicitly set on this object.
+         * <p>
+         * See also {@link #getRaw(ConfigKey).
          */
         @Beta
         Maybe<Object> getLocalRaw(ConfigKey<?> key);
@@ -96,6 +102,11 @@
          * Attempts to coerce the value for this config key, if available,
          * taking a default and {@link Maybe#absent absent} if the uncoerced
          * cannot be resolved within a short timeframe.
+         * <p>
+         * Note: if no value for the key is available, not even as a default,
+         * this returns a {@link Maybe#isPresent()} containing <code>null</code>
+         * (following the semantics of {@link #get(ConfigKey)} 
+         * rather than {@link #getRaw(ConfigKey)}).
          */
         @Beta
         <T> Maybe<T> getNonBlocking(ConfigKey<T> key);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
index eb4ff10..e55d6d9 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
@@ -44,6 +44,7 @@
 import org.apache.brooklyn.core.entity.AbstractApplication;
 import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityDynamicType;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
@@ -248,13 +249,13 @@
             }
             
             entity.tags().addTags(spec.getTags());
-            ((AbstractEntity)entity).configure(getConfigKeysFromSpecParameters(spec));
-            ((AbstractEntity)entity).configure(MutableMap.copyOf(spec.getFlags()));
+            addSpecParameters(spec, ((AbstractEntity)entity).getMutableEntityType());
             
+            ((AbstractEntity)entity).configure(MutableMap.copyOf(spec.getFlags()));
             for (Map.Entry<ConfigKey<?>, Object> entry : spec.getConfig().entrySet()) {
                 entity.config().set((ConfigKey)entry.getKey(), entry.getValue());
             }
-
+            
             Entity parent = spec.getParent();
             if (parent != null) {
                 parent = (parent instanceof AbstractEntity) ? ((AbstractEntity)parent).getProxyIfAvailable() : parent;
@@ -268,12 +269,11 @@
         }
     }
 
-    private <T extends Entity> List<ConfigKey<?>> getConfigKeysFromSpecParameters(EntitySpec<T> spec) {
-        List<ConfigKey<?>> configKeys = MutableList.of();
+    private void addSpecParameters(EntitySpec<?> spec, EntityDynamicType edType) {
         for (SpecParameter<?> param : spec.getParameters()) {
-            configKeys.add(param.getType());
+            edType.addConfigKey(param.getConfigKey());
+            if (param.getSensor()!=null) edType.addSensor(param.getSensor());
         }
-        return configKeys;
     }
 
     /**
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java
index f76baaa..2a26fa7 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java
@@ -19,11 +19,11 @@
 package org.apache.brooklyn.core.sensor;
 
 import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey.Builder;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
@@ -45,6 +45,7 @@
 */
 public abstract class AttributeSensorAndConfigKey<ConfigType,SensorType> extends BasicAttributeSensor<SensorType> 
         implements ConfigKey.HasConfigKey<ConfigType> {
+    
     private static final long serialVersionUID = -3103809215973264600L;
     private static final Logger log = LoggerFactory.getLogger(AttributeSensorAndConfigKey.class);
 
@@ -87,6 +88,10 @@
         configKey = ConfigKeys.newConfigKeyWithDefault(orig.configKey, 
                 TypeCoercions.coerce(defaultValue, orig.configKey.getTypeToken()));
     }
+    public AttributeSensorAndConfigKey(Builder<ConfigType> configKeyBuilder, TypeToken<SensorType> sensorType) {
+        super(sensorType, configKeyBuilder.getName(), configKeyBuilder.getDescription());
+        configKey = new BasicConfigKey<ConfigType>(configKeyBuilder);
+    }
 
     public ConfigKey<ConfigType> getConfigKey() { return configKey; }
     
@@ -106,7 +111,7 @@
         SensorType sensorValue = e.getAttribute(this);
         if (sensorValue!=null) return sensorValue;
         
-        ConfigType v = ((EntityLocal)e).getConfig(this);
+        ConfigType v = e.config().get(this);
         try {
             return convertConfigToSensor(v, e);
         } catch (Throwable t) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java
index 3de0de6..aa396d2 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java
@@ -29,6 +29,7 @@
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey;
 import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
 import org.apache.brooklyn.core.internal.BrooklynInitialization;
 import org.apache.brooklyn.core.location.Locations;
@@ -40,6 +41,7 @@
 import com.google.common.base.Optional;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Iterables;
+import com.google.common.reflect.TypeToken;
 
 /**
  * A {@link Sensor} describing a port on a system,
@@ -68,6 +70,10 @@
     public PortAttributeSensorAndConfigKey(PortAttributeSensorAndConfigKey orig, Object defaultValue) {
         super(orig, TypeCoercions.coerce(defaultValue, PortRange.class));
     }
+    public PortAttributeSensorAndConfigKey(BasicConfigKey.Builder<PortRange> builder) {
+        super(builder, TypeToken.of(Integer.class));
+    }
+    
     @Override
     protected Integer convertConfigToSensor(PortRange value, Entity entity) {
         if (value==null) return null;
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java
index 781cb0c..f09e72f 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java
@@ -98,6 +98,14 @@
 
     MethodEffector<Collection<Entity>> RESIZE_BY_DELTA = new MethodEffector<Collection<Entity>>(DynamicCluster.class, "resizeByDelta");
 
+    @SetFromFlag("restartMode")
+    ConfigKey<String> RESTART_MODE = ConfigKeys.newStringConfigKey(
+            "dynamiccluster.restartMode", 
+            "How this cluster should handle restarts; "
+            + "by default it is disallowed, but this key can specify a different mode. "
+            + "Modes supported by dynamic cluster are 'off', 'sequqential', or 'parallel'. "
+            + "However subclasses can define their own modes or may ignore this.", null);
+
     @SetFromFlag("quarantineFailedEntities")
     ConfigKey<Boolean> QUARANTINE_FAILED_ENTITIES = ConfigKeys.newBooleanConfigKey(
             "dynamiccluster.quarantineFailedEntities", "If true, will quarantine entities that fail to start; if false, will get rid of them (i.e. delete them)", true);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
index f434fcf..aaf06c5 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
@@ -47,6 +47,7 @@
 import org.apache.brooklyn.core.config.render.RendererHints;
 import org.apache.brooklyn.core.effector.Effectors;
 import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityPredicates;
 import org.apache.brooklyn.core.entity.factory.EntityFactory;
 import org.apache.brooklyn.core.entity.factory.EntityFactoryForLocation;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
@@ -522,7 +523,28 @@
 
     @Override
     public void restart() {
-        throw new UnsupportedOperationException();
+        String mode = getConfig(RESTART_MODE);
+        if (mode==null) {
+            throw new UnsupportedOperationException("Restart not supported for this cluster: "+RESTART_MODE.getName()+" is not configured.");
+        }
+        if ("off".equalsIgnoreCase(mode)) {
+            throw new UnsupportedOperationException("Restart not supported for this cluster.");
+        }
+        
+        if ("sequential".equalsIgnoreCase(mode)) {
+            ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
+            DynamicTasks.queue(Effectors.invocationSequential(Startable.RESTART, null, 
+                Iterables.filter(getChildren(), Predicates.and(Predicates.instanceOf(Startable.class), EntityPredicates.isManaged()))));
+        } else if ("parallel".equalsIgnoreCase(mode)) {
+            ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
+            DynamicTasks.queue(Effectors.invocationParallel(Startable.RESTART, null, 
+                Iterables.filter(getChildren(), Predicates.and(Predicates.instanceOf(Startable.class), EntityPredicates.isManaged()))));
+        } else {
+            throw new IllegalArgumentException("Unknown "+RESTART_MODE.getName()+" '"+mode+"'");
+        }
+        
+        DynamicTasks.waitForLast();
+        ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING);
     }
 
     @Override
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/feed/ssh/SshPollConfig.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/feed/ssh/SshPollConfig.java
index 8fec87f..7839fde 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/feed/ssh/SshPollConfig.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/feed/ssh/SshPollConfig.java
@@ -18,6 +18,8 @@
  */
 package org.apache.brooklyn.feed.ssh;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -29,6 +31,7 @@
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 
+import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
@@ -67,7 +70,12 @@
     public Map<String, String> getEnv() {
         return getEnvSupplier().get();
     }
+    
+    @SuppressWarnings("unused")
     public Supplier<Map<String,String>> getEnvSupplier() {
+        if (true) return new CombiningEnvSupplier(dynamicEnvironmentSupplier);
+        
+        // TODO Kept in case it's persisted; new code will not use this.
         return new Supplier<Map<String,String>>() {
             @Override
             public Map<String, String> get() {
@@ -82,13 +90,53 @@
                 }
                 return result;
             }
+            private void mergeEnvMaps(Map<String,String> supplied, Map<String,String> target) {
+                if (supplied==null) return;
+                // as the value is a string there is no need to look at deep merge behaviour
+                target.putAll(supplied);
+            }
         };
     }
     
-    protected void mergeEnvMaps(Map<String,String> supplied, Map<String,String> target) {
-        if (supplied==null) return;
-        // as the value is a string there is no need to look at deep merge behaviour
-        target.putAll(supplied);
+    private static class CombiningEnvSupplier implements Supplier<Map<String,String>> {
+        private final List<Supplier<Map<String, String>>> dynamicEnvironmentSupplier;
+        
+        public CombiningEnvSupplier(List<Supplier<Map<String,String>>> dynamicEnvironmentSupplier) {
+            this.dynamicEnvironmentSupplier = checkNotNull(dynamicEnvironmentSupplier, "dynamicEnvironmentSupplier");
+        }
+        @Override
+        public Map<String, String> get() {
+            Map<String,String> result = MutableMap.of();
+            for (Supplier<Map<String, String>> envS: dynamicEnvironmentSupplier) {
+                if (envS!=null) {
+                    Map<String, String> envM = envS.get();
+                    if (envM!=null) {
+                        mergeEnvMaps(envM, result);
+                    }
+                }
+            }
+            return result;
+        }
+        protected void mergeEnvMaps(Map<String,String> supplied, Map<String,String> target) {
+            if (supplied==null) return;
+            // as the value is a string there is no need to look at deep merge behaviour
+            target.putAll(supplied);
+        }
+        @Override
+        public int hashCode() {
+            return dynamicEnvironmentSupplier.hashCode();
+        }
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof CombiningEnvSupplier)) return false;
+            CombiningEnvSupplier o = (CombiningEnvSupplier) obj;
+            
+            return Objects.equal(dynamicEnvironmentSupplier, o.dynamicEnvironmentSupplier);
+        }
+        @Override
+        public String toString() {
+            return "CombiningEnvSupplier("+dynamicEnvironmentSupplier+")";
+        }
     }
 
     public SshPollConfig<T> command(String val) { return command(Suppliers.ofInstance(val)); }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
index 85c9537..3bc22e0 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
@@ -234,7 +234,7 @@
     public static List<FlagConfigKeyAndValueRecord> findAllParameterConfigKeys(List<SpecParameter<?>> parameters, ConfigBag input) {
         List<FlagConfigKeyAndValueRecord> output = new ArrayList<FlagUtils.FlagConfigKeyAndValueRecord>();
         for (SpecParameter<?> param : parameters) {
-            FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(null, param.getType(), input);
+            FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(null, param.getConfigKey(), input);
             if (record.isValuePresent())
                 output.add(record);
         }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
index f850fbd..872e713 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
@@ -122,10 +122,10 @@
         return coerce(value, TypeToken.of(targetType));
     }
 
-    /** @see #coerce(Object, Class) */
+    /** @see #coerce(Object, Class); allows a null value in the contents of the Maybe */
     public static <T> Maybe<T> tryCoerce(Object value, TypeToken<T> targetTypeToken) {
         try {
-            return Maybe.of( coerce(value, targetTypeToken) );
+            return Maybe.ofAllowingNull( coerce(value, targetTypeToken) );
         } catch (Throwable t) {
             Exceptions.propagateIfFatal(t);
             return Maybe.absent(t); 
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java
index 0aab7d5..ee15bec 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java
@@ -59,6 +59,8 @@
 import org.apache.brooklyn.util.exceptions.RuntimeInterruptedException;
 import org.apache.brooklyn.util.text.Identifiers;
 import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.time.CountdownTimer;
+import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -67,7 +69,6 @@
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.ExecutionList;
@@ -166,8 +167,26 @@
     }
     
     public void shutdownNow() {
+        shutdownNow(null);
+    }
+    
+    /** shuts down the executor, and if a duration is supplied awaits termination for that long.
+     * @return whether everything is terminated
+     */
+    @Beta
+    public boolean shutdownNow(Duration howLongToWaitForTermination) {
         runner.shutdownNow();
         delayedRunner.shutdownNow();
+        if (howLongToWaitForTermination!=null) {
+            CountdownTimer timer = howLongToWaitForTermination.countdownTimer();
+            try {
+                runner.awaitTermination(timer.getDurationRemaining().toMilliseconds(), TimeUnit.MILLISECONDS);
+                if (timer.isLive()) delayedRunner.awaitTermination(timer.getDurationRemaining().toMilliseconds(), TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                throw Exceptions.propagate(e);
+            }
+        }
+        return runner.isTerminated() && delayedRunner.isTerminated();
     }
     
     public void addListener(ExecutionListener listener) {
@@ -565,7 +584,15 @@
                 int subtasksReallyCancelled=0;
                 
                 if (task instanceof HasTaskChildren) {
-                    for (Task<?> child: ((HasTaskChildren)task).getChildren()) {
+                    // cancel tasks in reverse order --
+                    // it should be the case that if child1 is cancelled,
+                    // a parentTask should NOT call a subsequent child2,
+                    // but just in case, we cancel child2 first
+                    // NB: DST and others may apply their own recursive cancel behaviour
+                    MutableList<Task<?>> childrenReversed = MutableList.copyOf( ((HasTaskChildren)task).getChildren() );
+                    Collections.reverse(childrenReversed);
+                    
+                    for (Task<?> child: childrenReversed) {
                         if (log.isTraceEnabled()) {
                             log.trace("Cancelling "+child+" on recursive cancellation of "+task);
                         }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java
index 7c29bba..efd3001 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java
@@ -298,7 +298,7 @@
     public synchronized boolean cancel(TaskCancellationMode mode) {
         if (isDone()) return false;
         if (log.isTraceEnabled()) {
-            log.trace("BT cancelling "+this+" mode "+mode);
+            log.trace("BT cancelling "+this+" mode "+mode+", from thread "+Thread.currentThread());
         }
         cancelled = true;
         doCancel(mode);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
index 2809482..0f85c3f 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
@@ -42,6 +42,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -57,14 +58,25 @@
  */
 public class ValueResolver<T> implements DeferredSupplier<T> {
 
+    // TODO most of these usages should be removed when we have
+    // an ability to run resolution in a non-blocking mode
+    // (i.e. running resolution tasks in the same thread,
+    // or in a context where they can only wait on subtasks
+    // which are guaranteed to have the same constraint)
     /** 
      * Period to wait if we're expected to return real quick 
      * but we want fast things to have time to finish.
      * <p>
      * Timings are always somewhat arbitrary but this at least
      * allows some intention to be captured in code rather than arbitrary values. */
+    @Beta
     public static Duration REAL_QUICK_WAIT = Duration.millis(50);
     /** 
+     * Like {@link #REAL_QUICK_WAIT} but even smaller, for use when potentially
+     * resolving multiple items in sequence. */
+    @Beta
+    public static Duration REAL_REAL_QUICK_WAIT = Duration.millis(5);
+    /** 
      * Period to wait if we're expected to return quickly 
      * but we want to be a bit more generous for things to finish,
      * without letting a caller get annoyed. 
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
index e3bfe2d..46710fb 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
@@ -35,21 +35,23 @@
 import org.apache.brooklyn.api.sensor.EnricherSpec;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
-import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
 import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
 import org.apache.brooklyn.core.policy.AbstractPolicy;
 import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
-import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.core.test.entity.TestApplication;
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.core.test.entity.TestEntityImpl;
 import org.apache.brooklyn.core.test.policy.TestPolicy;
+import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
 import org.apache.brooklyn.util.net.Networking;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.brooklyn.util.time.Time;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
@@ -61,6 +63,8 @@
 
 public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
 
+    private static final Logger log = LoggerFactory.getLogger(ConfigKeyConstraintTest.class);
+    
     // ----------- Setup -----------------------------------------------------------------------------------------------
 
     @ImplementedBy(EntityWithNonNullConstraintImpl.class)
@@ -208,9 +212,8 @@
         app.start(ImmutableList.of(app.newSimulatedLocation()));
         TestEntity testEntity = app.addChild(EntitySpec.create(TestEntity.class));
         try {
-        testEntity.addChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class)
+            testEntity.addChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class)
                 .configure(EntityRequiringConfigKeyInRange.RANGE, -1));
-            Entities.manage(testEntity);
             fail("Expected exception when managing child with invalid config");
         } catch (Exception e) {
             Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
@@ -288,8 +291,7 @@
                     .configure("must-be-display-name", "Mr. Bag"));
             fail("Expected exception when managing entity with incorrect config");
         } catch (Exception e) {
-            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
-            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
+            Asserts.expectedFailureOfType(e, ConstraintViolationException.class);
         }
     }
 
@@ -297,12 +299,16 @@
     public void testQuickFutureResolved() {
         // Result of task is -1, outside of the range specified by the config key.
         try {
-            app.createAndManageChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class)
+            EntityRequiringConfigKeyInRange child = app.createAndManageChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class)
                     .configure(EntityRequiringConfigKeyInRange.RANGE, sleepingTask(Duration.ZERO, -1)));
-            fail("Expected exception when managing entity with incorrect config");
+            // may or may not fail above, depending on speed, but should fail if assert after forcing resolution
+            Object value = child.getConfig(EntityRequiringConfigKeyInRange.RANGE);
+            // NB the call above does not currently/necessarily apply validation
+            log.debug(JavaClassNames.niceClassAndMethod()+" got "+value+" for "+EntityRequiringConfigKeyInRange.RANGE+", now explicitly validating");
+            ConfigConstraints.assertValid(child);
+            fail("Expected exception when managing entity with incorrect config; instead passed assertion and got: "+value);
         } catch (Exception e) {
-            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
-            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
+            Asserts.expectedFailureOfType(e, ConstraintViolationException.class);
         }
     }
 
@@ -326,8 +332,12 @@
 
     // Supplies an entity, a policy and a location.
     @DataProvider(name = "brooklynObjects")
-    public Object[][] createBrooklynObjects() {
-        TestApplication app = ApplicationBuilder.newManagedApp(EntitySpec.create(TestApplication.class), LocalManagementContextForTests.newInstance());
+    public Object[][] createBrooklynObjects() throws Exception {
+        EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class)
+            .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution());
+        setUp();
+        TestApplication app = mgmt.getEntityManager().createEntity(appSpec);
+
         EntityRequiringConfigKeyInRange entity = app.createAndManageChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class)
                 .configure(EntityRequiringConfigKeyInRange.RANGE, 5));
         Policy policy = entity.policies().add(PolicySpec.create(TestPolicy.class));
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java
index 49cb2d6..30745f0 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java
@@ -103,7 +103,7 @@
     private void assertInput(SpecParameter<?> input, String label, boolean pinned, ConfigKey<?> type) {
         assertEquals(input.getLabel(), label);
         assertEquals(input.isPinned(), pinned);
-        assertEquals(input.getType(), type);
+        assertEquals(input.getConfigKey(), type);
     }
 
 }
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java
index 9f2eaaf..07ad81a 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java
@@ -56,7 +56,7 @@
         SpecParameter<?> input = parse(name);
         assertEquals(input.getLabel(), name);
         assertTrue(input.isPinned());
-        ConfigKey<?> type = input.getType();
+        ConfigKey<?> type = input.getConfigKey();
         assertEquals(type.getName(), name);
         assertEquals(type.getTypeToken(), TypeToken.of(String.class));
         assertNull(type.getDefaultValue());
@@ -70,8 +70,8 @@
         String name = "minRam";
         SpecParameter<?> input = parse(ImmutableMap.of("name", name));
         assertEquals(input.getLabel(), name);
-        assertEquals(input.getType().getName(), name);
-        assertEquals(input.getType().getTypeToken(), TypeToken.of(String.class));
+        assertEquals(input.getConfigKey().getName(), name);
+        assertEquals(input.getConfigKey().getTypeToken(), TypeToken.of(String.class));
     }
 
     @Test
@@ -99,7 +99,7 @@
         assertEquals(input.getLabel(), label);
         assertTrue(input.isPinned());
 
-        ConfigKey<?> type = input.getType();
+        ConfigKey<?> type = input.getConfigKey();
         assertEquals(type.getName(), name);
         assertEquals(type.getTypeToken(), TypeToken.of(String.class));
         assertEquals(type.getDefaultValue(), defaultValue);
@@ -123,7 +123,7 @@
         assertEquals(input.getLabel(), name);
         assertTrue(input.isPinned());
 
-        ConfigKey<?> type = input.getType();
+        ConfigKey<?> type = input.getConfigKey();
         assertEquals(type.getName(), name);
         assertEquals(type.getDefaultValue(), defaultValue);
         assertEquals(type.getDescription(), description);
@@ -137,7 +137,7 @@
         SpecParameter<?> input = parse(ImmutableMap.of(
                 "name", name,
                 "constraints", ImmutableList.of(constraint)));
-        ConfigKey<?> type = input.getType();
+        ConfigKey<?> type = input.getConfigKey();
         assertConstraint(type.getConstraint(), StringPredicates.isNonBlank());
     }
 
@@ -153,7 +153,7 @@
         SpecParameter<?> input = parse(ImmutableMap.of(
                 "name", name,
                 "type", BasicSpecParameterFromListTest.class.getName()));
-        assertEquals(input.getType().getTypeToken(), TypeToken.of(BasicSpecParameterFromListTest.class));
+        assertEquals(input.getConfigKey().getTypeToken(), TypeToken.of(BasicSpecParameterFromListTest.class));
     }
 
     @Test(expectedExceptions = IllegalArgumentException.class)
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/feed/ssh/SshFeedIntegrationTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/feed/ssh/SshFeedIntegrationTest.java
index 4559247..416ab9d 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/feed/ssh/SshFeedIntegrationTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/feed/ssh/SshFeedIntegrationTest.java
@@ -26,7 +26,7 @@
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.core.entity.Attributes;
-import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityAsserts;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.entity.EntityInternal.FeedSupport;
 import org.apache.brooklyn.core.sensor.Sensors;
@@ -37,14 +37,11 @@
 import org.apache.brooklyn.feed.ssh.SshPollConfig;
 import org.apache.brooklyn.feed.ssh.SshPollValue;
 import org.apache.brooklyn.feed.ssh.SshValueFunctions;
-import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.stream.Streams;
 import org.apache.brooklyn.util.text.StringFunctions;
 import org.apache.brooklyn.util.text.StringPredicates;
-import org.apache.brooklyn.util.time.Duration;
-import org.apache.brooklyn.util.time.Time;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -68,7 +65,7 @@
 
     private LocalhostMachineProvisioningLocation loc;
     private SshMachineLocation machine;
-    private EntityLocal entity;
+    private TestEntity entity;
     private SshFeed feed;
     
     @BeforeMethod(alwaysRun=true)
@@ -104,7 +101,7 @@
                         .onSuccess(SshValueFunctions.stdout()))
                 .build();
         
-        EntityTestUtils.assertAttributeEventuallyNonNull(entity2, SENSOR_STRING);
+        EntityAsserts.assertAttributeEventuallyNonNull(entity2, SENSOR_STRING);
         String val = entity2.getAttribute(SENSOR_STRING);
         Assert.assertTrue(val.contains("hello"), "val="+val);
         Assert.assertEquals(val.trim(), "hello");
@@ -135,7 +132,7 @@
                         .onSuccess(SshValueFunctions.exitStatus()))
                 .build();
 
-        EntityTestUtils.assertAttributeEqualsEventually(entity, SENSOR_INT, 123);
+        EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_INT, 123);
     }
     
     @Test(groups="Integration")
@@ -148,7 +145,7 @@
                         .onSuccess(SshValueFunctions.stdout()))
                 .build();
         
-        EntityTestUtils.assertAttributeEventually(entity, SENSOR_STRING, 
+        EntityAsserts.assertAttributeEventually(entity, SENSOR_STRING, 
             Predicates.compose(Predicates.equalTo("hello"), StringFunctions.trim()));
     }
 
@@ -164,7 +161,7 @@
                         .onFailure(SshValueFunctions.stderr()))
                 .build();
         
-        EntityTestUtils.assertAttributeEventually(entity, SENSOR_STRING, StringPredicates.containsLiteral(cmd));
+        EntityAsserts.assertAttributeEventually(entity, SENSOR_STRING, StringPredicates.containsLiteral(cmd));
     }
     
     @Test(groups="Integration")
@@ -181,7 +178,7 @@
                             }}))
                 .build();
         
-        EntityTestUtils.assertAttributeEventually(entity, SENSOR_STRING, StringPredicates.containsLiteral("Exit status 123"));
+        EntityAsserts.assertAttributeEventually(entity, SENSOR_STRING, StringPredicates.containsLiteral("Exit status 123"));
     }
     
     @Test(groups="Integration")
@@ -202,10 +199,10 @@
             }));
 
         // TODO would be nice to hook in and assert no errors
-        EntityTestUtils.assertAttributeEqualsContinually(entity2, SENSOR_STRING, null);
+        EntityAsserts.assertAttributeEqualsContinually(entity2, SENSOR_STRING, null);
 
         entity2.sensors().set(Attributes.SERVICE_UP, true);
-        EntityTestUtils.assertAttributeEventually(entity2, SENSOR_STRING, StringPredicates.containsLiteral("hello"));
+        EntityAsserts.assertAttributeEventually(entity2, SENSOR_STRING, StringPredicates.containsLiteral("hello"));
     }
 
     
@@ -235,10 +232,10 @@
                         .onSuccess(SshValueFunctions.stdout()))
                 .build();
         
-        EntityTestUtils.assertAttributeEventuallyNonNull(entity2, SENSOR_STRING);        
+        EntityAsserts.assertAttributeEventuallyNonNull(entity2, SENSOR_STRING);
         final String val1 = assertDifferentOneInOutput(entity2);
         
-        EntityTestUtils.assertAttributeEventually(entity2, SENSOR_STRING, Predicates.not(Predicates.equalTo(val1)));        
+        EntityAsserts.assertAttributeEventually(entity2, SENSOR_STRING, Predicates.not(Predicates.equalTo(val1)));        
         final String val2 = assertDifferentOneInOutput(entity2);
         log.info("vals from dynamic sensors are: "+val1.trim()+" and "+val2.trim());
     }
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/feed/ssh/SshFeedTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/feed/ssh/SshFeedTest.java
new file mode 100644
index 0000000..a3f22dd
--- /dev/null
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/feed/ssh/SshFeedTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.feed.ssh;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Functions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+public class SshFeedTest extends BrooklynAppUnitTestSupport {
+
+    private static final Logger log = LoggerFactory.getLogger(SshFeedTest.class);
+    
+    final static AttributeSensor<String> SENSOR_STRING = Sensors.newStringSensor("aString", "");
+    final static AttributeSensor<String> SENSOR_STRING2 = Sensors.newStringSensor("aString2", "");
+
+    private LocalhostMachineProvisioningLocation loc;
+    private EntityLocal entity;
+    private SshFeed feed;
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        loc = app.newLocalhostProvisioningLocation();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        RecordingSshMachineLocation.execScriptCalls.clear();
+    }
+
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        if (feed != null) feed.stop();
+        super.tearDown();
+        if (loc != null) Streams.closeQuietly(loc);
+        RecordingSshMachineLocation.execScriptCalls.clear();
+    }
+    
+    @Test(groups="Integration") // integration because slow 
+    public void testSharesCallWhenSameCommand() throws Exception {
+        final RecordingSshMachineLocation recordingMachine = mgmt.getLocationManager().createLocation(LocationSpec.create(RecordingSshMachineLocation.class));
+        app.start(ImmutableList.of(recordingMachine));
+        
+        final String cmd = "myCommand";
+        
+        feed = SshFeed.builder()
+                .period(Duration.PRACTICALLY_FOREVER)
+                .entity(entity)
+                .poll(new SshPollConfig<String>(SENSOR_STRING)
+                        .env(ImmutableMap.of("mykey", "myval"))
+                        .command(cmd)
+                        .onSuccess(Functions.constant("success")))
+                .poll(new SshPollConfig<String>(SENSOR_STRING2)
+                        .env(ImmutableMap.of("mykey", "myval"))
+                        .command(cmd)
+                        .onSuccess(Functions.constant("success2")))
+                .build();
+        
+        // Expect it to only execute once (i.e. share exec result for both sensors).
+        // Wait several seconds, in case it takes a while to do the second exec.
+        Asserts.succeedsEventually(new Runnable() {
+            public void run() {
+                assertEquals(RecordingSshMachineLocation.execScriptCalls, ImmutableList.of(ImmutableList.of(cmd)));
+            }});
+        Asserts.succeedsContinually(ImmutableMap.of("timeout", Duration.FIVE_SECONDS), new Runnable() {
+            public void run() {
+                assertEquals(RecordingSshMachineLocation.execScriptCalls, ImmutableList.of(ImmutableList.of(cmd)));
+            }});
+    }
+
+    @Test
+    public void testDifferentCallsWhenDifferentCommands() throws Exception {
+        final RecordingSshMachineLocation recordingMachine = mgmt.getLocationManager().createLocation(LocationSpec.create(RecordingSshMachineLocation.class));
+        app.start(ImmutableList.of(recordingMachine));
+        
+        final String cmd = "myCommand";
+        final String cmd2 = "myCommand2";
+        
+        feed = SshFeed.builder()
+                .period(Duration.PRACTICALLY_FOREVER)
+                .entity(entity)
+                .poll(new SshPollConfig<String>(SENSOR_STRING)
+                        .command(cmd)
+                        .onSuccess(Functions.constant("success")))
+                .poll(new SshPollConfig<String>(SENSOR_STRING2)
+                        .command(cmd2)
+                        .onSuccess(Functions.constant("success")))
+                .build();
+        
+        // Expect it to execute the different commands (i.e. not share)
+        Asserts.succeedsEventually(new Runnable() {
+            public void run() {
+                assertEquals(ImmutableSet.copyOf(RecordingSshMachineLocation.execScriptCalls), ImmutableSet.of(ImmutableList.of(cmd), ImmutableList.of(cmd2)));
+            }});
+    }
+
+    @Test
+    public void testDifferentCallsWhenDifferentEnv() throws Exception {
+        final RecordingSshMachineLocation recordingMachine = mgmt.getLocationManager().createLocation(LocationSpec.create(RecordingSshMachineLocation.class));
+        app.start(ImmutableList.of(recordingMachine));
+        
+        final String cmd = "myCommand";
+        
+        feed = SshFeed.builder()
+                .period(Duration.PRACTICALLY_FOREVER)
+                .entity(entity)
+                .poll(new SshPollConfig<String>(SENSOR_STRING)
+                        .env(ImmutableMap.of("mykey", "myval"))
+                        .command(cmd)
+                        .onSuccess(Functions.constant("success")))
+                .poll(new SshPollConfig<String>(SENSOR_STRING2)
+                        .env(ImmutableMap.of("mykey", "myval2"))
+                        .command(cmd)
+                        .onSuccess(Functions.constant("success")))
+                .build();
+        
+        // Expect it to execute the command twice, with different envs (i.e. not share)
+        Asserts.succeedsEventually(new Runnable() {
+            public void run() {
+                assertEquals(RecordingSshMachineLocation.execScriptCalls, ImmutableList.of(ImmutableList.of(cmd), ImmutableList.of(cmd)));
+            }});
+    }
+
+    public static class RecordingSshMachineLocation extends SshMachineLocation {
+        public static List<List<String>> execScriptCalls = Lists.newCopyOnWriteArrayList();
+
+        @Override 
+        public int execScript(String summary, List<String> cmds) {
+            execScriptCalls.add(cmds);
+            return 0;
+        }
+        @Override 
+        public int execScript(Map<String,?> props, String summaryForLogging, List<String> cmds) {
+            execScriptCalls.add(cmds);
+            return 0;
+        }
+        @Override 
+        public int execScript(String summaryForLogging, List<String> cmds, Map<String,?> env) {
+            execScriptCalls.add(cmds);
+            return 0;
+        }
+        @Override 
+        public int execScript(Map<String,?> props, String summaryForLogging, List<String> cmds, Map<String,?> env) {
+            execScriptCalls.add(cmds);
+            return 0;
+        }
+    }
+}
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java
index 364870a..ac5c11c 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java
@@ -26,7 +26,6 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 import org.apache.brooklyn.api.mgmt.HasTaskChildren;
 import org.apache.brooklyn.api.mgmt.Task;
@@ -39,7 +38,6 @@
 import org.apache.brooklyn.util.core.task.TaskInternal.TaskCancellationMode;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.math.MathPredicates;
-import org.apache.brooklyn.util.time.CountdownTimer;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.brooklyn.util.time.Time;
 import org.slf4j.Logger;
@@ -64,7 +62,7 @@
     private static final Logger log = LoggerFactory.getLogger(DynamicSequentialTaskTest.class);
     
     public static final Duration TIMEOUT = Duration.TEN_SECONDS;
-    public static final Duration TINY_TIME = Duration.millis(20);
+    public static final Duration TINY_TIME = Duration.millis(1);
     
     BasicExecutionManager em;
     BasicExecutionContext ec;
@@ -88,7 +86,11 @@
     
     @AfterMethod(alwaysRun=true)
     public void tearDown() throws Exception {
-        if (em != null) em.shutdownNow();
+        if (em != null) {
+            // need to await termination, otherwise interrupted-but-still-running threads 
+            // may update the cancellations/messages and interfere with subsequent tests
+            Assert.assertTrue(em.shutdownNow(Duration.FIVE_SECONDS));
+        }
     }
 
     @Test
@@ -139,6 +141,7 @@
                         Thread.sleep(duration.toMillisecondsRoundingUp());
                     }
                 } catch (InterruptedException e) {
+                    log.info("releasing semaphore on interruption after saying "+message);
                     cancellations.release();
                     throw Exceptions.propagate(e);
                 }
@@ -159,7 +162,8 @@
     }
     
     public Task<String> sayTask(String message, Duration duration, String message2) {
-        return Tasks.<String>builder().displayName("say:"+message).body(sayCallable(message, duration, message2)).build();
+        return Tasks.<String>builder().displayName("say:"+message+(duration!=null ? ":wait("+duration+")" : "")+(message2!=null ? ":"+message2 : ""))
+            .body(sayCallable(message, duration, message2)).build();
     }
     
     public <T> Task<T> submitting(final Task<T> task) {
@@ -193,38 +197,59 @@
                 sayTask("2a", Duration.THIRTY_SECONDS, "2b"),
                 sayTask("3"));
         ec.submit(t);
-        
+
+        // wait for 2 to start, saying "2a", and the first interruptible block is when it waits for its 30s
         waitForMessages(Predicates.compose(MathPredicates.greaterThanOrEqual(2), CollectionFunctionals.sizeFunction()), TIMEOUT);
         Assert.assertEquals(messages, Arrays.asList("1", "2a"));
-        Time.sleep(Duration.millis(50));
+        
+        // now cancel, and make sure we get the right behaviour
         t.cancel(true);
         Assert.assertTrue(t.isDone());
-        // 2 should get cancelled, and invoke the cancellation semaphore
+        // 2 should get cancelled, and invoke the cancellation semaphore, but not say 2b
         // 3 should get cancelled and not run at all
-        Assert.assertEquals(messages, Arrays.asList("1", "2a"));
         
-        // Need to ensure that 2 has been started; race where we might cancel it before its run method
-        // is even begun. Hence doing "2a; pause; 2b" where nothing is interruptable before pause.
+        // cancel(..) currently cancels everything in the tree in the calling thread
+        // so we could even assert task3.isCancelled() now
+        // but not sure we will guarantee that for subtasks, so weaker assertion
+        // that it is eventually cancelled, and that it for sure never starts
+        
+        // message list is still 1, 2a
+        Assert.assertEquals(messages, Arrays.asList("1", "2a"));
+        // And 2 when cancelled should release the semaphore
+        log.info("testCancelled waiting on semaphore; permits left is "+cancellations.availablePermits());
         Assert.assertTrue(cancellations.tryAcquire(10, TimeUnit.SECONDS));
+        log.info("testCancelled acquired semaphore; permits left is "+cancellations.availablePermits());
         
         Iterator<Task<?>> ci = ((HasTaskChildren)t).getChildren().iterator();
+        // 1 completed fine
         Assert.assertEquals(ci.next().get(), "1");
+        // 2 is cancelled -- cancelled flag should always be set *before* the interrupt sent
+        // (and that released the semaphore above, even if thread is not completed, so this should be set)
         Task<?> task2 = ci.next();
         Assert.assertTrue(task2.isBegun());
         Assert.assertTrue(task2.isDone());
         Assert.assertTrue(task2.isCancelled());
-        
+
         Task<?> task3 = ci.next();
+        // 3 is being cancelled in the thread which cancelled 2, and should either be
+        // *before* 2 was cancelled or *not run* because the parent was cancelled 
+        // so we shouldn't need to wait ... but if we did:
+//        Asserts.eventually(Suppliers.ofInstance(task3), TaskPredicates.isDone());
+        Assert.assertTrue(task3.isDone());
+        Assert.assertTrue(task3.isCancelled());
         Assert.assertFalse(task3.isBegun());
-        Assert.assertTrue(task2.isDone());
-        Assert.assertTrue(task2.isCancelled());
-        
-        // but we do _not_ get a mutex from task3 as it does not run (is not interrupted)
+        // messages unchanged
+        Assert.assertEquals(messages, Arrays.asList("1", "2a"));
+        // no further mutexes should be available (ie 3 should not run)
+        // TODO for some reason this was observed to fail on the jenkins box (2016-01)
+        // but i can't see why; have added logging in case it happens again
         Assert.assertEquals(cancellations.availablePermits(), 0);
     }
     
     @Test
     public void testCancellationModeAndSubmitted() throws Exception {
+        // seems actually to be the logging which causes this to take ~50ms ?
+        
         doTestCancellationModeAndSubmitted(true, TaskCancellationMode.DO_NOT_INTERRUPT, false, false);
         
         doTestCancellationModeAndSubmitted(true, TaskCancellationMode.INTERRUPT_TASK_AND_ALL_SUBMITTED_TASKS, true, true);
@@ -296,27 +321,14 @@
                 @Override public Number get() { return t1.getEndTimeUtc(); }}, 
                 MathPredicates.<Number>greaterThanOrEqual(0));
         } else {
-            Time.sleep(Duration.millis(5));
+            Time.sleep(TINY_TIME);
             Assert.assertFalse(t1.isCancelled());
             Assert.assertFalse(t1.isDone());
         }
     }
 
     protected void waitForMessages(Predicate<? super List<String>> predicate, Duration timeout) throws Exception {
-        long endtime = System.currentTimeMillis() + timeout.toMilliseconds();
-        synchronized (messages) {
-            while (true) {
-                if (predicate.apply(messages)) {
-                    return;
-                }
-                long waittime = endtime - System.currentTimeMillis();
-                if (waittime > 0) {
-                    messages.wait(waittime);
-                } else {
-                    throw new TimeoutException("Timeout after "+timeout+"; messages="+messages+"; predicate="+predicate);
-                }
-            }
-        }
+        Asserts.eventuallyOnNotify(messages, predicate, timeout);
     }
     
     protected Task<String> monitorableTask(final String id) {
@@ -352,14 +364,7 @@
         monitorableJobSemaphoreMap.get(id).release();
     }
     protected void waitForMessage(final String id) {
-        CountdownTimer timer = CountdownTimer.newInstanceStarted(TIMEOUT);
-        synchronized (messages) {
-            while (!timer.isExpired()) {
-                if (messages.contains(id)) return;
-                timer.waitOnForExpiryUnchecked(messages);
-            }
-        }
-        Assert.fail("Did not see message "+id);
+        Asserts.eventuallyOnNotify(messages, CollectionFunctionals.contains(id), TIMEOUT);
     }
     protected void releaseAndWaitForMonitorableJob(final String id) {
         releaseMonitorableJob(id);
diff --git a/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/ha/ConnectionFailureDetectorTest.java b/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/ha/ConnectionFailureDetectorTest.java
index 1b34c11..25f8d54 100644
--- a/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/ha/ConnectionFailureDetectorTest.java
+++ b/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/ha/ConnectionFailureDetectorTest.java
@@ -37,19 +37,24 @@
 import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.policy.ha.HASensors.FailureDescriptor;
 import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.time.Duration;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
-import org.apache.brooklyn.policy.ha.HASensors.FailureDescriptor;
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.net.HostAndPort;
 
+@Test(groups="Integration")
+// can fail if a firewall is in place on high-numbered ephemeral ports;
+// at least one report of *test* failures, as in no events coming in;
+// log unavailable but the test has been passing everywhere else so we
+// suspect some environments don't support this test
 public class ConnectionFailureDetectorTest {
 
     private static final int TIMEOUT_MS = 30*1000;
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java
index 74625fd..514d9c9 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java
@@ -68,15 +68,21 @@
         Set<EffectorSummary> effectors = Sets.newTreeSet(SummaryComparators.nameComparator());
 
         EntitySpec<?> spec = null;
-
         try {
-            spec = (EntitySpec<?>) b.getCatalog().createSpec((CatalogItem) item);
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            // the raw type isn't needed according to eclipse IDE, but jenkins maven fails without it;
+            // must be a java version or compiler thing. don't remove even though it looks okay without it!
+            EntitySpec<?> specRaw = (EntitySpec<?>) b.getCatalog().createSpec((CatalogItem) item);
+            spec = specRaw;
             EntityDynamicType typeMap = BrooklynTypes.getDefinedEntityType(spec.getType());
             EntityType type = typeMap.getSnapshot();
 
             AtomicInteger paramPriorityCnt = new AtomicInteger();
-            for (SpecParameter<?> input: spec.getParameters())
+            for (SpecParameter<?> input: spec.getParameters()) {
                 config.add(EntityTransformer.entityConfigSummary(input, paramPriorityCnt));
+                if (input.getSensor()!=null)
+                    sensors.add(SensorTransformer.sensorSummaryForCatalog(input.getSensor()));
+            }
             for (Sensor<?> x: type.getSensors())
                 sensors.add(SensorTransformer.sensorSummaryForCatalog(x));
             for (Effector<?> x: type.getEffectors())
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
index 2d9f8a0..f5079b9 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
@@ -159,7 +159,7 @@
         // which results in correctly ordered items on the wire (as a list). Clients which use the java bindings
         // though will push the items in an unordered set - so give them means to recover the correct order.
         Double priority = input.isPinned() ? Double.valueOf(paramPriorityCnt.incrementAndGet()) : null;
-        return entityConfigSummary(input.getType(), input.getLabel(), priority, null);
+        return entityConfigSummary(input.getConfigKey(), input.getLabel(), priority, null);
     }
 
 }
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
index d243833..4ffeb1a 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
@@ -107,40 +107,43 @@
             skipStart = entityStarted.or(false);
         }
         if (!skipStart) {
-            Optional<Boolean> locationInstalled = Optional.fromNullable(getLocation().getConfig(BrooklynConfigKeys.SKIP_ENTITY_INSTALLATION));
-            Optional<Boolean> entityInstalled = Optional.fromNullable(entity.getConfig(BrooklynConfigKeys.SKIP_ENTITY_INSTALLATION));
-            boolean skipInstall = locationInstalled.or(entityInstalled).or(false);
-            if (!skipInstall) {
-                DynamicTasks.queue("copy-pre-install-resources", new Runnable() { public void run() {
-                    waitForConfigKey(BrooklynConfigKeys.PRE_INSTALL_RESOURCES_LATCH);
-                    copyPreInstallResources();
-                }});
+            DynamicTasks.queue("install", new Runnable() { public void run() {
 
-                DynamicTasks.queue("pre-install", new Runnable() { public void run() {
-                    preInstall();
-                }});
+                Optional<Boolean> locationInstalled = Optional.fromNullable(getLocation().getConfig(BrooklynConfigKeys.SKIP_ENTITY_INSTALLATION));
+                Optional<Boolean> entityInstalled = Optional.fromNullable(entity.getConfig(BrooklynConfigKeys.SKIP_ENTITY_INSTALLATION));
+                boolean skipInstall = locationInstalled.or(entityInstalled).or(false);
+                if (!skipInstall) {
+                    DynamicTasks.queue("copy-pre-install-resources", new Runnable() { public void run() {
+                        waitForConfigKey(BrooklynConfigKeys.PRE_INSTALL_RESOURCES_LATCH);
+                        copyPreInstallResources();
+                    }});
 
-                DynamicTasks.queue("pre-install-command", new Runnable() { public void run() {
-                    runPreInstallCommand();
-                }});
-                DynamicTasks.queue("setup", new Runnable() { public void run() {
-                    waitForConfigKey(BrooklynConfigKeys.SETUP_LATCH);
-                    setup();
-                }});
+                    DynamicTasks.queue("pre-install", new Runnable() { public void run() {
+                        preInstall();
+                    }});
 
-                DynamicTasks.queue("copy-install-resources", new Runnable() { public void run() {
-                    waitForConfigKey(BrooklynConfigKeys.INSTALL_RESOURCES_LATCH);
-                    copyInstallResources();
-                }});
+                    DynamicTasks.queue("pre-install-command", new Runnable() { public void run() {
+                        runPreInstallCommand();
+                    }});
+                    DynamicTasks.queue("setup", new Runnable() { public void run() {
+                        waitForConfigKey(BrooklynConfigKeys.SETUP_LATCH);
+                        setup();
+                    }});
 
-                DynamicTasks.queue("install", new Runnable() { public void run() {
-                    waitForConfigKey(BrooklynConfigKeys.INSTALL_LATCH);
-                    install();
-                }});
-            }
+                    DynamicTasks.queue("copy-install-resources", new Runnable() { public void run() {
+                        waitForConfigKey(BrooklynConfigKeys.INSTALL_RESOURCES_LATCH);
+                        copyInstallResources();
+                    }});
 
-            DynamicTasks.queue("post-install-command", new Runnable() { public void run() {
-                runPostInstallCommand();
+                    DynamicTasks.queue("install (main)", new Runnable() { public void run() {
+                        waitForConfigKey(BrooklynConfigKeys.INSTALL_LATCH);
+                        install();
+                    }});
+                }
+
+                DynamicTasks.queue("post-install-command", new Runnable() { public void run() {
+                    runPostInstallCommand();
+                }});
             }});
 
             DynamicTasks.queue("customize", new Runnable() { public void run() {
@@ -148,22 +151,24 @@
                 customize();
             }});
 
-            DynamicTasks.queue("copy-runtime-resources", new Runnable() { public void run() {
-                waitForConfigKey(BrooklynConfigKeys.RUNTIME_RESOURCES_LATCH);
-                copyRuntimeResources();
-            }});
-
-            DynamicTasks.queue("pre-launch-command", new Runnable() { public void run() {
-                runPreLaunchCommand();
-            }});
-
             DynamicTasks.queue("launch", new Runnable() { public void run() {
-                waitForConfigKey(BrooklynConfigKeys.LAUNCH_LATCH);
-                launch();
-            }});
+                DynamicTasks.queue("copy-runtime-resources", new Runnable() { public void run() {
+                    waitForConfigKey(BrooklynConfigKeys.RUNTIME_RESOURCES_LATCH);
+                    copyRuntimeResources();
+                }});
 
-            DynamicTasks.queue("post-launch-command", new Runnable() { public void run() {
-                runPostLaunchCommand();
+                DynamicTasks.queue("pre-launch-command", new Runnable() { public void run() {
+                    runPreLaunchCommand();
+                }});
+
+                DynamicTasks.queue("launch (main)", new Runnable() { public void run() {
+                    waitForConfigKey(BrooklynConfigKeys.LAUNCH_LATCH);
+                    launch();
+                }});
+
+                DynamicTasks.queue("post-launch-command", new Runnable() { public void run() {
+                    runPostLaunchCommand();
+                }});
             }});
         }
 
@@ -187,6 +192,7 @@
     public abstract void customize();
     public abstract void runPreLaunchCommand();
     public abstract void launch();
+    /** Only run if launch is run (if start is not skipped). */
     public abstract void runPostLaunchCommand();
 
     @Override
@@ -195,7 +201,8 @@
     }
 
     /**
-     * Implement this method in child classes to add some post-launch behavior
+     * Implement this method in child classes to add some post-launch behavior.
+     * This is run even if start is skipped and launch is not run.
      */
     public void postLaunch() {}
 
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java
index 91aa294..cb4149a 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java
@@ -118,7 +118,8 @@
         entity().initDriver(machine);
 
         // Note: must only apply config-sensors after adding to locations and creating driver; 
-        // otherwise can't do things like acquire free port from location, or allowing driver to set up ports
+        // otherwise can't do things like acquire free port from location
+        // or allowing driver to set up ports; but must be careful in init not to block on these!
         super.preStartCustom(machine);
         
         entity().preStart();
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
index c62cc3d..ace4ebe 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
@@ -548,7 +548,7 @@
         boolean isRunningResult = false;
         long delay = 100;
         Exception firstFailure = null;
-        while (!isRunningResult && !timer.isExpired()) {
+        while (!isRunningResult && timer.isNotExpired()) {
             Time.sleep(delay);
             try {
                 isRunningResult = driver.isRunning();
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java
index dd01a69..9c955ed 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java
@@ -21,11 +21,12 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolver;
+import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.objs.BrooklynObjectInternal.ConfigurationSupportInternal;
 import org.apache.brooklyn.entity.software.base.lifecycle.ScriptHelper;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
 import org.apache.brooklyn.util.collections.MutableMap;
@@ -54,15 +55,30 @@
      */
     @Override
     protected String getInstallLabelExtraSalt() {
+        // run non-blocking in case a value set later is used (e.g. a port)
+        Integer hash = hashCodeIfResolved(SoftwareProcess.DOWNLOAD_URL.getConfigKey(), 
+            VanillaSoftwareProcess.INSTALL_COMMAND, SoftwareProcess.SHELL_ENVIRONMENT);
         
-        Maybe<Object> url = getEntity().getConfigRaw(SoftwareProcess.DOWNLOAD_URL, true);
-        String installCommand = getEntity().getConfig(VanillaSoftwareProcess.INSTALL_COMMAND);
-        Map<String, Object> shellEnv = getEntity().getConfig(VanillaSoftwareProcess.SHELL_ENVIRONMENT);
+        // if any of the above blocked then we must make a unique install label,
+        // as other yet-unknown config is involved 
+        if (hash==null) return Identifiers.makeRandomId(8);
         
-        // TODO a user-friendly hash would be nice, but tricky since we don't want it to be too long or contain path chars
-        return Identifiers.makeIdFromHash(Objects.hash(url.or(null), installCommand, shellEnv));
+        // a user-friendly hash is nice, but tricky since it would have to be short; 
+        // go with a random one unless it's totally blank
+        if (hash==0) return "default";
+        return Identifiers.makeIdFromHash(hash);
     }
     
+    private Integer hashCodeIfResolved(ConfigKey<?> ...keys) {
+        int hash = 0;
+        for (ConfigKey<?> k: keys) {
+            Maybe<?> value = ((ConfigurationSupportInternal)getEntity().config()).getNonBlocking(k);
+            if (value.isAbsent()) return null;
+            hash = hash*31 + (value.get()==null ? 0 : value.get().hashCode());
+        }
+        return hash;
+    }
+
     @Override
     public void install() {
         Maybe<Object> url = getEntity().getConfigRaw(SoftwareProcess.DOWNLOAD_URL, true);
diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java
index 77175d2..e3dfb12 100644
--- a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java
+++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java
@@ -38,6 +38,7 @@
 import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
 import org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy;
 import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.CollectionFunctionals;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -75,12 +76,14 @@
         entitiesAdded = Sets.newLinkedHashSet();
         entitiesRemoved = Sets.newLinkedHashSet();
         mgmt.addEntitySetListener(new CollectionChangeListener<Entity>() {
-            @Override public void onItemAdded(Entity item) {
-                entitiesAdded.add(item);
-            }
-            @Override public void onItemRemoved(Entity item) {
-                entitiesRemoved.add(item);
-            }});
+            @Override public void onItemAdded(Entity item) { addToSetAndNotify(entitiesAdded, item); }
+            @Override public void onItemRemoved(Entity item) { addToSetAndNotify(entitiesRemoved, item); }});
+    }
+    private static <T> void addToSetAndNotify(Set<T> items, T item) {
+        synchronized (items) {
+            items.add(item);
+            items.notifyAll();
+        }
     }
 
     @Test
@@ -179,7 +182,7 @@
 
     protected void assertSize(int targetSize, int quarantineSize, final int deletedSize) {
         assertSize(targetSize, quarantineSize);
-        assertEquals(entitiesRemoved.size(), deletedSize, "removed="+entitiesRemoved);
+        Asserts.eventuallyOnNotify(entitiesRemoved, CollectionFunctionals.sizeEquals(deletedSize));
     }
     
     protected void assertSize(int targetSize, int quarantineSize) {
diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/jmx/JmxService.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/jmx/JmxService.java
index 2e23789..397aff3 100644
--- a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/jmx/JmxService.java
+++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/jmx/JmxService.java
@@ -21,7 +21,6 @@
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
-import java.util.Random;
 
 import javax.management.InstanceAlreadyExistsException;
 import javax.management.MBeanNotificationInfo;
@@ -47,6 +46,7 @@
 import org.apache.brooklyn.core.entity.Attributes;
 import org.apache.brooklyn.entity.java.UsesJmx;
 import org.apache.brooklyn.feed.jmx.JmxHelper;
+import org.apache.brooklyn.test.NetworkingTestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -68,8 +68,10 @@
     private String url;
 
     public JmxService() throws Exception {
-        this("localhost", 28000 + (int)Math.floor(new Random().nextDouble() * 1000));
-        logger.warn("use of deprecated default host and port in JmxService");
+        this("localhost", NetworkingTestUtils.randomPortAround(28000));
+        
+        // TODO why this message if the constructor is not actually deprecated, and it seems useful?
+        //logger.warn("use of deprecated default host and port in JmxService");
     }
     
     /**
@@ -151,10 +153,12 @@
      * @throws MBeanRegistrationException 
      * @throws InstanceAlreadyExistsException 
      */
+    @SuppressWarnings({ "rawtypes" })
     public GeneralisedDynamicMBean registerMBean(Map initialAttributes, String name) throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, MalformedObjectNameException, NullPointerException {
         return registerMBean(initialAttributes, ImmutableMap.of(), name);
     }
     
+    @SuppressWarnings({ "rawtypes", "unchecked" })
     public GeneralisedDynamicMBean registerMBean(Map initialAttributes, Map operations, String name) throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, MalformedObjectNameException, NullPointerException {
         GeneralisedDynamicMBean mbean = new GeneralisedDynamicMBean(initialAttributes, operations);
         server.registerMBean(mbean, new ObjectName(name));
diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/system_service/SystemServiceEnricherTest.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/system_service/SystemServiceEnricherTest.java
index 10e2e15..70c0da5 100644
--- a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/system_service/SystemServiceEnricherTest.java
+++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/system_service/SystemServiceEnricherTest.java
@@ -86,10 +86,10 @@
     }
 
     private void waitFailed(VanillaSoftwareProcess proc) {
-        Asserts.eventually(ImmutableMap.of("timeout", Duration.FIVE_MINUTES), Suppliers.ofInstance(proc), EntityPredicates.attributeEqualTo(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE));
+        Asserts.eventually(Suppliers.ofInstance(proc), EntityPredicates.attributeEqualTo(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE), Duration.FIVE_MINUTES);
     }
 
     private void waitHealthy(VanillaSoftwareProcess proc) {
-        Asserts.eventually(ImmutableMap.of("timeout", Duration.FIVE_MINUTES), Suppliers.ofInstance(proc), EntityPredicates.attributeEqualTo(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING));
+        Asserts.eventually(Suppliers.ofInstance(proc), EntityPredicates.attributeEqualTo(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING), Duration.FIVE_MINUTES);
     }
 }
diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/feed/jmx/JmxFeedTest.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/feed/jmx/JmxFeedTest.java
index a90d657..d8df35b 100644
--- a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/feed/jmx/JmxFeedTest.java
+++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/feed/jmx/JmxFeedTest.java
@@ -65,6 +65,7 @@
 import org.apache.brooklyn.entity.software.base.test.jmx.JmxService;
 import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.test.NetworkingTestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
@@ -114,7 +115,13 @@
         @Override public void init() {
             sensors().set(Attributes.HOSTNAME, "localhost");
             sensors().set(UsesJmx.JMX_PORT, 
-                    LocalhostMachineProvisioningLocation.obtainPort(PortRanges.fromString("40123+")));
+                    LocalhostMachineProvisioningLocation.obtainPort(PortRanges.fromString(
+                        // just doing "40123+" was not enough to avoid collisions (on 40125),
+                        // observed on jenkins, not sure why but 
+                        // maybe something else had a UDP connection we weren't detected,
+                        // or the static lock our localhost uses was being bypassed;
+                        // this should improve things (2016-01)
+                        NetworkingTestUtils.randomPortAround(40000)+"+")));
             // only supports no-agent, at the moment
             config().set(UsesJmx.JMX_AGENT_MODE, JmxAgentModes.NONE);
             sensors().set(UsesJmx.RMI_REGISTRY_PORT, -1);  // -1 means to use the JMX_PORT only
diff --git a/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/NetworkingTestUtils.java b/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/NetworkingTestUtils.java
index 7b48eaf..3cb4ba9 100644
--- a/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/NetworkingTestUtils.java
+++ b/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/NetworkingTestUtils.java
@@ -22,13 +22,13 @@
 
 import java.util.Map;
 
-import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.net.Networking;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
 import com.google.common.collect.ImmutableMap;
 
 public class NetworkingTestUtils {
@@ -65,4 +65,14 @@
             assertTrue(Networking.isPortAvailable(entry.getValue()), errmsg);
         }
     }
+    
+    /** Returns a port not in use somewhere around the seed;
+     * this is not a foolproof way to prevent collisions, 
+     * but strikes a good balance of traceability (different callers will use different ranges)
+     * and collision avoidance, esp when combined with <code>Localhost...obtain(thisResult+"+");</code>.
+     */
+    @Beta
+    public static int randomPortAround(int seed) {
+        return Networking.nextAvailablePort( seed + (int)Math.floor(Math.random() * 1000) );
+    }
 }
diff --git a/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/WebAppMonitor.java b/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/WebAppMonitor.java
index d804312..b957a5b 100644
--- a/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/WebAppMonitor.java
+++ b/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/WebAppMonitor.java
@@ -184,7 +184,7 @@
         return this;
     }
     public WebAppMonitor waitForAtLeastOneAttempt() {
-        return waitForAtLeastOneAttempt(Asserts.DEFAULT_TIMEOUT);
+        return waitForAtLeastOneAttempt(Asserts.DEFAULT_LONG_TIMEOUT);
     }
     public WebAppMonitor waitForAtLeastOneAttempt(Duration timeout) {
         Asserts.succeedsEventually(MutableMap.of("timeout", timeout), new Runnable() {
diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
index 15aa76e..fac3142 100644
--- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
+++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
@@ -37,7 +37,9 @@
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.javalang.JavaClassNames;
+import org.apache.brooklyn.util.repeat.Repeater;
 import org.apache.brooklyn.util.text.StringPredicates;
+import org.apache.brooklyn.util.time.CountdownTimer;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -46,6 +48,7 @@
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
@@ -68,11 +71,24 @@
 @Beta
 public class Asserts {
 
-    /**
-     * The default timeout for assertions - 30s.
-     * Alter in individual tests by giving a "timeout" entry in method flags.
+    /** 
+     * Timeout for use when something should happen within several seconds,
+     * but there might be network calls or computation so {@link #DEFAULT_SHORT_TIMEOUT} is not applicable.
      */
-    public static final Duration DEFAULT_TIMEOUT = Duration.THIRTY_SECONDS;
+    public static final Duration DEFAULT_LONG_TIMEOUT = Duration.THIRTY_SECONDS;
+    
+    /** 
+     * Timeout for use when waiting for other threads to finish.
+     * <p>
+     * Long enough for parallel execution to catch up, 
+     * even on overloaded mediocre test boxes most of the time,
+     * but short enough not to irritate you when your test is failing. */
+    public static final Duration DEFAULT_SHORT_TIMEOUT = Duration.ONE_SECOND;
+    
+    /** @deprecated since 0.9.0 use {@link #DEFAULT_LONG_TIMEOUT} */ @Deprecated
+    public static final Duration DEFAULT_TIMEOUT = DEFAULT_LONG_TIMEOUT;
+    
+    private static final Duration DEFAULT_SHORT_PERIOD = Repeater.DEFAULT_REAL_QUICK_PERIOD;
 
     private static final Logger log = LoggerFactory.getLogger(Asserts.class);
 
@@ -730,58 +746,99 @@
 
     // --- new routines
     
+    /**  As {@link #eventually(Supplier, Predicate, Duration, Duration, String)} with defaults. */
     public static <T> void eventually(Supplier<? extends T> supplier, Predicate<T> predicate) {
-        eventually(ImmutableMap.<String,Object>of(), supplier, predicate);
+        eventually(supplier, predicate, null, null, null);
     }
     
+    /** @deprecated since 0.9.0 use {@link #eventually(Supplier, Predicate, Duration, Duration, String)} */ @Deprecated
     public static <T> void eventually(Map<String,?> flags, Supplier<? extends T> supplier, Predicate<T> predicate) {
         eventually(flags, supplier, predicate, (String)null);
     }
-    
+    /** @deprecated since 0.9.0 use {@link #eventually(Supplier, Predicate, Duration, Duration, String)} */ @Deprecated
     public static <T> void eventually(Map<String,?> flags, Supplier<? extends T> supplier, Predicate<T> predicate, String errMsg) {
-        Duration timeout = toDuration(flags.get("timeout"), Duration.ONE_SECOND);
-        Duration period = toDuration(flags.get("period"), Duration.millis(10));
-        long periodMs = period.toMilliseconds();
-        long startTime = System.currentTimeMillis();
-        long expireTime = startTime+timeout.toMilliseconds();
-        
-        boolean first = true;
-        T supplied = supplier.get();
-        while (first || System.currentTimeMillis() <= expireTime) {
-            supplied = supplier.get();
-            if (predicate.apply(supplied)) {
-                return;
-            }
-            first = false;
-            if (periodMs > 0) sleep(periodMs);
-        }
-        fail("supplied="+supplied+"; predicate="+predicate+(errMsg!=null?"; "+errMsg:""));
+        eventually(supplier, predicate, toDuration(flags.get("timeout"), null), toDuration(flags.get("period"), null), errMsg);
     }
     
-    // TODO improve here -- these methods aren't very useful without timeouts
+    /**  As {@link #eventually(Supplier, Predicate, Duration, Duration, String)} with default. */
+    public static <T> void eventually(Supplier<? extends T> supplier, Predicate<T> predicate, Duration timeout) {
+        eventually(supplier, predicate, timeout, null, null);
+    }
+    
+    /** Asserts that eventually the supplier gives a value accepted by the predicate. 
+     * Tests periodically and succeeds as soon as the supplier gives an allowed value.
+     * Other arguments can be null.
+     * 
+     * @param supplier supplies the value to test, such as {@link Suppliers#ofInstance(Object)} for a constant 
+     * @param predicate the {@link Predicate} to apply to each value given by the supplier
+     * @param timeout how long to wait, default {@link #DEFAULT_SHORT_TIMEOUT}
+     * @param period how often to check, default quite often so you won't notice but letting the CPU do work
+     * @param errMsg an error message to display if not satisfied, in addition to the last-tested supplied value and the predicate
+     */
+    public static <T> void eventually(Supplier<? extends T> supplier, Predicate<T> predicate, Duration timeout, Duration period, String errMsg) {
+        if (timeout==null) timeout = DEFAULT_SHORT_TIMEOUT;
+        if (period==null) period = DEFAULT_SHORT_PERIOD;
+        CountdownTimer timeleft = timeout.countdownTimer();
+        
+        T supplied;
+        int count = 0;
+        do {
+            if (count++ > 0) Duration.sleep(period);
+            supplied = supplier.get();
+            if (predicate.apply(supplied)) return;
+        } while (timeleft.isNotExpired());
+
+        fail("Expected: eventually "+predicate+"; got most recently: "+supplied
+            +" (waited "+timeleft.getDurationElapsed()+", checked "+count+")"
+            +(errMsg!=null?"; "+errMsg:""));
+    }
+    
+    /**  As {@link #continually(Supplier, Predicate, Duration, Duration, String)} with defaults. */
     public static <T> void continually(Supplier<? extends T> supplier, Predicate<T> predicate) {
         continually(ImmutableMap.<String,Object>of(), supplier, predicate);
     }
 
+    /** @deprecated since 0.9.0 use {@link #eventually(Supplier, Predicate, Duration, Duration, String)} */ @Deprecated
     public static <T> void continually(Map<String,?> flags, Supplier<? extends T> supplier, Predicate<? super T> predicate) {
-        continually(flags, supplier, predicate, (String)null);
+        continually(flags, supplier, predicate, null);
     }
 
+    /** @deprecated since 0.9.0 use {@link #eventually(Supplier, Predicate, Duration, Duration, String)} */ @Deprecated
     public static <T> void continually(Map<String,?> flags, Supplier<? extends T> supplier, Predicate<T> predicate, String errMsg) {
-        Duration duration = toDuration(flags.get("timeout"), Duration.ONE_SECOND);
-        Duration period = toDuration(flags.get("period"), Duration.millis(10));
-        long periodMs = period.toMilliseconds();
-        long startTime = System.currentTimeMillis();
-        long expireTime = startTime+duration.toMilliseconds();
-        
-        boolean first = true;
-        while (first || System.currentTimeMillis() <= expireTime) {
-            assertTrue(predicate.apply(supplier.get()), "supplied="+supplier.get()+"; predicate="+predicate+(errMsg!=null?"; "+errMsg:""));
-            if (periodMs > 0) sleep(periodMs);
-            first = false;
-        }
+        continually(supplier, predicate, toDuration(flags.get("timeout"), toDuration(flags.get("duration"), null)), 
+            toDuration(flags.get("period"), null), null);
     }
+    /** 
+     * Asserts that continually the supplier gives a value accepted by the predicate. 
+     * Tests periodically and fails if the supplier gives a disallowed value.
+     * Other arguments can be null.
+     * 
+     * @param supplier supplies the value to test, such as {@link Suppliers#ofInstance(Object)} for a constant 
+     * @param predicate the {@link Predicate} to apply to each value given by the supplier
+     * @param duration how long to test for, default {@link #DEFAULT_SHORT_TIMEOUT}
+     * @param period how often to check, default quite often to minimise chance of missing a flashing violation but letting the CPU do work
+     * @param errMsg an error message to display if not satisfied, in addition to the last-tested supplied value and the predicate
+     */
+    public static <T> void continually(Supplier<? extends T> supplier, Predicate<T> predicate, Duration duration, Duration period, String errMsg) {
+        if (duration==null) duration = DEFAULT_SHORT_TIMEOUT;
+        if (period==null) period = DEFAULT_SHORT_PERIOD;
 
+        CountdownTimer timeleft = duration.countdownTimer();
+        
+        T supplied;
+        int count = 0;
+        do {
+            if (count > 0) Duration.sleep(period);
+            supplied = supplier.get();
+            if (!predicate.apply(supplied)) {
+                fail("Expected: continually "+predicate+"; got violation: "+supplied
+                    // tell timing if it worked the first time and then failed
+                    +(count > 0 ? " (after "+timeleft.getDurationElapsed()+", successfully checked "+count+")" : "")
+                    +(errMsg!=null?"; "+errMsg:""));
+            }
+            count++;
+        } while (timeleft.isNotExpired());
+    }
     
     /**
      * @see #succeedsContinually(Map, Callable)
@@ -818,6 +875,9 @@
         }
     }
 
+    // TODO flags are ugly; remove this in favour of something strongly typed,
+    // e.g. extending Repeater and taking the extra semantics.
+    // TODO remove the #succeedsEventually in favour of #eventually (and same for continually)
     /**
      * Convenience method for cases where we need to test until something is true.
      *
@@ -827,7 +887,7 @@
      * <ul>
      * <li>abortOnError (boolean, default true)
      * <li>abortOnException - (boolean, default false)
-     * <li>timeout - (a Duration or an integer in millis, defaults to {@link Asserts#DEFAULT_TIMEOUT})
+     * <li>timeout - (a Duration or an integer in millis, defaults to {@link Asserts#DEFAULT_LONG_TIMEOUT})
      * <li>period - (a Duration or an integer in millis, for fixed retry time; if not set, defaults to exponentially increasing from 1 to 500ms)
      * <li>minPeriod - (a Duration or an integer in millis; only used if period not explicitly set; the minimum period when exponentially increasing; defaults to 1ms)
      * <li>maxPeriod - (a Duration or an integer in millis; only used if period not explicitly set; the maximum period when exponentially increasing; defaults to 500ms)
@@ -849,7 +909,7 @@
         boolean logException = get(flags, "logException", true);
 
         // To speed up tests, default is for the period to start small and increase...
-        Duration duration = toDuration(flags.get("timeout"), DEFAULT_TIMEOUT);
+        Duration duration = toDuration(flags.get("timeout"), DEFAULT_LONG_TIMEOUT);
         Duration fixedPeriod = toDuration(flags.get("period"), null);
         Duration minPeriod = (fixedPeriod != null) ? fixedPeriod : toDuration(flags.get("minPeriod"), Duration.millis(1));
         Duration maxPeriod = (fixedPeriod != null) ? fixedPeriod : toDuration(flags.get("maxPeriod"), Duration.millis(500));
@@ -915,7 +975,8 @@
     public static <T> T succeedsContinually(Callable<T> c) {
         return succeedsContinually(ImmutableMap.<String,Object>of(), c);
     }
-    
+
+    // TODO unify with "continually"; see also eventually, some of those options might be useful
     public static <T> T succeedsContinually(Map<?,?> flags, Callable<T> job) {
         Duration duration = toDuration(flags.get("timeout"), Duration.ONE_SECOND);
         Duration period = toDuration(flags.get("period"), Duration.millis(10));
@@ -1240,4 +1301,50 @@
         throw new RuntimeException(t);
     }
 
+    /** As {@link #eventuallyOnNotify(Object, Supplier, Predicate, Duration, boolean)} with default timeout. */
+    public static <T> void eventuallyOnNotify(Object notifyTarget, Supplier<T> supplier, Predicate<T> predicate) {
+        eventuallyOnNotify(notifyTarget, supplier, predicate, null);
+    }
+    
+    /** as {@link #eventually(Supplier, Predicate)} for cases where an object is notified;
+     * more efficient as it waits on the notify target object. 
+     * See also the simpler {@link #eventuallyOnNotify(Object, Predicate)} when looking at a collection which is getting notified.
+     * Timeout defaults to {@link #DEFAULT_SHORT_TIMEOUT}. 
+     * <p>
+     * This synchronizes on the notify target for the duration of the wait, 
+     * including while getting and checking the value, so as not to miss any notification. */
+    public static <T> void eventuallyOnNotify(Object notifyTarget, Supplier<T> supplier, Predicate<T> predicate, Duration timeout) {
+        T supplied = null;
+        if (timeout==null) timeout = DEFAULT_SHORT_TIMEOUT;
+        CountdownTimer remaining = timeout.countdownTimer();
+        int checks = 0;
+        synchronized (notifyTarget) {
+            do {
+                if (checks>0) {
+                    remaining.waitOnForExpiryUnchecked(notifyTarget);
+                }
+                supplied = supplier.get();
+                if (predicate.apply(supplied)) return;
+                checks++;
+            } while (remaining.isNotExpired());
+        }
+        
+        // should get 2 checks, 1 before and 1 after, if no notifications; if more, tell the user
+        fail("Expected: eventually "+predicate+"; got most recently: "+supplied+
+            " (waited "+remaining.getDurationElapsed()+
+                (checks>2 ? "; notification count "+(checks-2) : "")+
+            ")");
+    }
+
+    /** Convenience for {@link #eventuallyOnNotify(Object, Supplier, Predicate, Duration, boolean)} 
+     * when the notify target and the value under test are the same. */
+    public static <T> void eventuallyOnNotify(T object, Predicate<T> predicate, Duration timeout) {
+        eventuallyOnNotify(object, Suppliers.ofInstance(object), predicate, timeout);
+    }
+
+    /** As {@link #eventuallyOnNotify(Object, Predicate, Duration)} with the default duration of {@link #eventuallyOnNotify(Object, Supplier, Predicate)}. */
+    public static <T> void eventuallyOnNotify(T object, Predicate<T> predicate) {
+        eventuallyOnNotify(object, Suppliers.ofInstance(object), predicate, null);
+    }
+
 }
diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/net/Networking.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/net/Networking.java
index fb988c7..85c87a0 100644
--- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/net/Networking.java
+++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/net/Networking.java
@@ -18,6 +18,8 @@
  */
 package org.apache.brooklyn.util.net;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 import java.io.IOException;
 import java.net.DatagramSocket;
 import java.net.InetAddress;
@@ -26,7 +28,6 @@
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.SocketException;
-import java.net.URI;
 import java.net.UnknownHostException;
 import java.util.Arrays;
 import java.util.Enumeration;
@@ -48,8 +49,6 @@
 import com.google.common.net.HostAndPort;
 import com.google.common.primitives.UnsignedBytes;
 
-import static com.google.common.base.Preconditions.checkArgument;
-
 public class Networking {
 
     private static final Logger log = LoggerFactory.getLogger(Networking.class);
diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/time/CountdownTimer.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/time/CountdownTimer.java
index 508657d..fea8ee6 100644
--- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/time/CountdownTimer.java
+++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/time/CountdownTimer.java
@@ -55,7 +55,7 @@
         return limit;
     }
 
-    /** return how long the timer has been running (longer than limit if {@link #isExpired()}) */
+    /** return how long the timer has been running (may be longer than {@link #getLimit()} if {@link #isExpired()}) */
     public Duration getDurationElapsed() {
         return Duration.nanos(stopwatch.elapsed(TimeUnit.NANOSECONDS));
     }
@@ -65,15 +65,28 @@
         return Duration.millis(limit.toMilliseconds() - stopwatch.elapsed(TimeUnit.MILLISECONDS));
     }
 
-    /** true iff the timer has been running for the duration specified at creation time */
+    /** true iff the timer has run for more than the duration specified at creation time */
     public boolean isExpired() {
         return stopwatch.elapsed(TimeUnit.MILLISECONDS) > limit.toMilliseconds();
     }
-    
-    /** true iff timer is running (even if it is expired) */
-    public boolean isRunning() {
+
+    /** true iff {@link #isNotPaused()} and not {@link #isExpired()} */
+    public boolean isLive() {
+        return isNotPaused() && isNotExpired();
+    }
+
+    /** true iff not {@link #isExpired()} */
+    public boolean isNotExpired() {
+        return !isExpired();
+    }
+
+    /** false if started or paused, true otherwise (ie the timer is counting down, even if it is expired) */
+    public boolean isNotPaused() {
         return stopwatch.isRunning();
     }
+
+    /** @deprecated since 0.9.0 use better named {@link #isNotPaused()} */ @Deprecated
+    public boolean isRunning() { return isNotPaused(); }
     
     // --- constructor methods
     
diff --git a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/time/CountdownTimerTest.java b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/time/CountdownTimerTest.java
index 5541caa..45995da 100644
--- a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/time/CountdownTimerTest.java
+++ b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/time/CountdownTimerTest.java
@@ -50,6 +50,9 @@
         
         CountdownTimer timer = SIMPLE_DURATION.countdownTimer();
         assertFalse(timer.isExpired());
+        assertTrue(timer.isNotExpired());
+        assertTrue(timer.isLive());
+        assertTrue(timer.isNotPaused());
         assertTrue(timer.getDurationElapsed().toMilliseconds() <= OVERHEAD_MS, "elapsed="+timer.getDurationElapsed().toMilliseconds());
         assertTrue(timer.getDurationRemaining().toMilliseconds() >= TOTAL_TIME_MS - OVERHEAD_MS, "remaining="+timer.getDurationElapsed().toMilliseconds());
         
@@ -60,6 +63,10 @@
         
         Time.sleep(Duration.millis(SECOND_SLEEP_TIME_MS));
         assertTrue(timer.isExpired());
+        assertFalse(timer.isNotExpired());
+        assertFalse(timer.isLive());
+        assertTrue(timer.isNotPaused());
+
     }
     
     public void testNotify() throws InterruptedException {
diff --git a/brooklyn-ui/LICENSE b/brooklyn-ui/LICENSE
index ca60394..58c78f1 100644
--- a/brooklyn-ui/LICENSE
+++ b/brooklyn-ui/LICENSE
@@ -282,6 +282,13 @@
   Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
   Copyright (c) Vitaly Puzrin (2011-2015)
 
+This project includes the software: marked.js
+  Available at: https://github.com/chjj/marked
+  Developed by: Christopher Jeffrey (https://github.com/chjj)
+  Version used: 0.3.1
+  Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
+  Copyright (c) Christopher Jeffrey (2011-2014)
+
 This project includes the software: moment.js
   Available at: http://momentjs.com
   Developed by: Tim Wood (http://momentjs.com)
@@ -323,27 +330,13 @@
       Arpad Borsos (2012)
     Used under the BSD 2-Clause license.
 
-This project includes the software: Swagger JS
-  Available at: https://github.com/wordnik/swagger-js
-  Inclusive of: swagger.js
-  Version used: 1.0.1
-  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
-  Copyright (c) SmartBear Software (2011-2015)
-
 This project includes the software: Swagger UI
-  Available at: https://github.com/wordnik/swagger-ui
-  Inclusive of: swagger-ui.js
-  Version used: 1.0.1
+  Available at: https://github.com/swagger-api/swagger-ui
+  Inclusive of: swagger*.{js,css,html}
+  Version used: 2.1.4
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
   Copyright (c) SmartBear Software (2011-2015)
 
-This project includes the software: typeahead.js
-  Available at: https://github.com/twitter/typeahead.js
-  Developed by: Twitter, Inc (http://twitter.com)
-  Version used: 0.10.5
-  Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
-  Copyright (c) Twitter, Inc. and other contributors (2013-2014)
-
 This project includes the software: underscore.js
   Available at: http://underscorejs.org
   Developed by: DocumentCloud Inc. (http://www.documentcloud.org/)
@@ -352,14 +345,6 @@
   Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
   Copyright (c) Jeremy Ashkenas, DocumentCloud Inc. (2009-2013)
 
-This project includes the software: underscore.js:1.7.0
-  Available at: http://underscorejs.org
-  Developed by: DocumentCloud Inc. (http://www.documentcloud.org/)
-  Inclusive of: underscore*.{js,map}
-  Version used: 1.7.0
-  Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
-  Copyright (c) Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors (2009-2014)
-
 This project includes the software: ZeroClipboard
   Available at: http://zeroclipboard.org/
   Developed by: ZeroClipboard contributors (https://github.com/zeroclipboard)
diff --git a/brooklyn-ui/src/main/license/files/LICENSE b/brooklyn-ui/src/main/license/files/LICENSE
index 3b3b73d..58c78f1 100644
--- a/brooklyn-ui/src/main/license/files/LICENSE
+++ b/brooklyn-ui/src/main/license/files/LICENSE
@@ -330,11 +330,12 @@
       Arpad Borsos (2012)
     Used under the BSD 2-Clause license.
 
-This project includes the software: swagger
-  Used under the following license: <no license info>
-
-This project includes the software: swagger-ui
-  Used under the following license: <no license info>
+This project includes the software: Swagger UI
+  Available at: https://github.com/swagger-api/swagger-ui
+  Inclusive of: swagger*.{js,css,html}
+  Version used: 2.1.4
+  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+  Copyright (c) SmartBear Software (2011-2015)
 
 This project includes the software: underscore.js
   Available at: http://underscorejs.org
diff --git a/brooklyn-ui/src/main/license/source-inclusions.yaml b/brooklyn-ui/src/main/license/source-inclusions.yaml
index 2adf949..8c1945c 100644
--- a/brooklyn-ui/src/main/license/source-inclusions.yaml
+++ b/brooklyn-ui/src/main/license/source-inclusions.yaml
@@ -20,9 +20,10 @@
 # extras file for org.heneveld.license-audit-maven-plugin
 # listing projects from which *source* files are included
 
-- id: jquery-core
-- id: swagger
-- id: swagger-ui
+- id: jquery-core:1.7.2
+- id: swagger-ui:2.1.4
+# we use other versions of the above in other projs
+
 - id: jquery.wiggle.min.js
 - id: require.js
 - id: require.js/r.js