This closes #1148
diff --git a/LICENSE b/LICENSE
index ca60394..ef288dc 100644
--- a/LICENSE
+++ b/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,19 +330,11 @@
       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
+  Used under the following license: <no license info>
 
-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
-  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
+  Used under the following license: <no license info>
 
 This project includes the software: typeahead.js
   Available at: https://github.com/twitter/typeahead.js
diff --git a/brooklyn-dist/dist/licensing/extras-files b/brooklyn-dist/dist/licensing/extras-files
index bb4a3b3..cbba9c5 100644
--- a/brooklyn-dist/dist/licensing/extras-files
+++ b/brooklyn-dist/dist/licensing/extras-files
@@ -1 +1 @@
-../jsgui/src/main/license/source-inclusions.yaml:../cli/src/main/license/source-inclusions.yaml
+../../brooklyn-ui/src/main/license/source-inclusions.yaml:../../brooklyn-server/server-cli/src/main/license/source-inclusions.yaml
diff --git a/brooklyn-dist/dist/licensing/licenses/jsgui/BSD-2-Clause b/brooklyn-dist/dist/licensing/licenses/brooklyn-ui/BSD-2-Clause
similarity index 100%
rename from brooklyn-dist/dist/licensing/licenses/jsgui/BSD-2-Clause
rename to brooklyn-dist/dist/licensing/licenses/brooklyn-ui/BSD-2-Clause
diff --git a/brooklyn-dist/dist/licensing/licenses/jsgui/BSD-3-Clause b/brooklyn-dist/dist/licensing/licenses/brooklyn-ui/BSD-3-Clause
similarity index 100%
rename from brooklyn-dist/dist/licensing/licenses/jsgui/BSD-3-Clause
rename to brooklyn-dist/dist/licensing/licenses/brooklyn-ui/BSD-3-Clause
diff --git a/brooklyn-dist/dist/licensing/licenses/cli/MIT b/brooklyn-dist/dist/licensing/licenses/brooklyn-ui/MIT
similarity index 100%
copy from brooklyn-dist/dist/licensing/licenses/cli/MIT
copy to brooklyn-dist/dist/licensing/licenses/brooklyn-ui/MIT
diff --git a/brooklyn-dist/dist/licensing/licenses/jsgui/MIT b/brooklyn-dist/dist/licensing/licenses/jsgui/MIT
deleted file mode 100644
index 71dfb45..0000000
--- a/brooklyn-dist/dist/licensing/licenses/jsgui/MIT
+++ /dev/null
@@ -1,20 +0,0 @@
-The MIT License ("MIT")
-
-  Permission is hereby granted, free of charge, to any person obtaining a copy
-  of this software and associated documentation files (the "Software"), to deal
-  in the Software without restriction, including without limitation the rights
-  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the Software is
-  furnished to do so, subject to the following conditions:
-  
-  The above copyright notice and this permission notice shall be included in
-  all copies or substantial portions of the Software.
-  
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-  THE SOFTWARE.
-  
diff --git a/brooklyn-dist/dist/licensing/licenses/cli/MIT b/brooklyn-dist/dist/licensing/licenses/server-cli/MIT
similarity index 100%
rename from brooklyn-dist/dist/licensing/licenses/cli/MIT
rename to brooklyn-dist/dist/licensing/licenses/server-cli/MIT
diff --git a/brooklyn-dist/dist/licensing/projects-with-custom-licenses b/brooklyn-dist/dist/licensing/projects-with-custom-licenses
index ebf210c..97839fc 100644
--- a/brooklyn-dist/dist/licensing/projects-with-custom-licenses
+++ b/brooklyn-dist/dist/licensing/projects-with-custom-licenses
@@ -1,2 +1,2 @@
-../jsgui
-../cli
+../../brooklyn-ui
+../../brooklyn-server/server-cli
diff --git a/brooklyn-dist/dist/src/main/license/files/LICENSE b/brooklyn-dist/dist/src/main/license/files/LICENSE
index b280a7d..39db9db 100644
--- a/brooklyn-dist/dist/src/main/license/files/LICENSE
+++ b/brooklyn-dist/dist/src/main/license/files/LICENSE
@@ -232,21 +232,33 @@
   Used under the following license: Eclipse Public License, version 1.0 (http://www.eclipse.org/legal/epl-v10.html)
 
 This project includes the software: com.fasterxml.jackson.core
-  Available at: http://wiki.fasterxml.com/JacksonHome
+  Available at: http://github.com/FasterXML/jackson https://github.com/FasterXML/jackson
   Developed by: FasterXML (http://fasterxml.com/)
-  Version used: 2.4.2; 2.4.0
+  Version used: 2.4.5
+  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+This project includes the software: com.fasterxml.jackson.dataformat
+  Available at: https://github.com/FasterXML/jackson http://wiki.fasterxml.com/JacksonExtensionXmlDataBinding
+  Developed by: FasterXML (http://fasterxml.com/)
+  Version used: 2.4.5
+  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+This project includes the software: com.fasterxml.jackson.datatype
+  Available at: http://wiki.fasterxml.com/JacksonModuleJoda
+  Developed by: FasterXML (http://fasterxml.com/)
+  Version used: 2.4.5
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
 This project includes the software: com.fasterxml.jackson.jaxrs
   Available at: http://wiki.fasterxml.com/JacksonHome
   Developed by: FasterXML (http://fasterxml.com/)
-  Version used: 2.4.2
+  Version used: 2.4.5
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
 This project includes the software: com.fasterxml.jackson.module
   Available at: http://wiki.fasterxml.com/JacksonJAXBAnnotations
   Developed by: FasterXML (http://fasterxml.com/)
-  Version used: 2.4.2
+  Version used: 2.4.5
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
 This project includes the software: com.google.code.findbugs
@@ -329,13 +341,13 @@
 This project includes the software: com.sun.jersey
   Available at: https://jersey.java.net/
   Developed by: Oracle Corporation (http://www.oracle.com/)
-  Version used: 1.18.1
+  Version used: 1.19
   Used under the following license: Common Development and Distribution License, version 1.1 (http://glassfish.java.net/public/CDDL+GPL_1_1.html)
 
 This project includes the software: com.sun.jersey.contribs
   Available at: https://jersey.java.net/
   Developed by: Oracle Corporation (http://www.oracle.com/)
-  Version used: 1.18.1
+  Version used: 1.19
   Used under the following license: Common Development and Distribution License, version 1.1 (http://glassfish.java.net/public/CDDL+GPL_1_1.html)
 
 This project includes the software: com.thoughtworks.xstream
@@ -344,11 +356,6 @@
   Version used: 1.4.7
   Used under the following license: BSD License (http://xstream.codehaus.org/license.html)
 
-This project includes the software: com.wordnik
-  Available at: https://github.com/wordnik/swagger-core
-  Version used: 1.0.1
-  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html)
-
 This project includes the software: commons-beanutils
   Available at: http://commons.apache.org/proper/commons-beanutils/
   Developed by: The Apache Software Foundation (http://www.apache.org/)
@@ -409,6 +416,11 @@
   Version used: 0.1.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
+  Available at: https://github.com/swagger-api/swagger-core
+  Version used: 1.5.3
+  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html)
+
 This project includes the software: javax.annotation
   Available at: https://jcp.org/en/jsr/detail?id=250
   Version used: 1.0
@@ -426,8 +438,9 @@
   Used under the following license: CDDL + GPLv2 with classpath exception (https://glassfish.dev.java.net/nonav/public/CDDL+GPL.html)
 
 This project includes the software: javax.validation
-  Version used: 1.0.0.GA
-  Used under the following license: Apache License, version 2.0 (in-project reference: license.txt)
+  Available at: http://beanvalidation.org
+  Version used: 1.1.0.Final
+  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
 
 This project includes the software: javax.ws.rs
   Available at: https://jsr311.java.net/
@@ -435,6 +448,12 @@
   Version used: 1.1.1
   Used under the following license: CDDL License (http://www.opensource.org/licenses/cddl1.php)
 
+This project includes the software: joda-time
+  Available at: http://joda-time.sourceforge.net
+  Developed by: Joda.org (http://www.joda.org)
+  Version used: 2.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: jQuery JavaScript Library
   Available at: http://jquery.com/
   Developed by: The jQuery Foundation (http://jquery.org/)
@@ -495,6 +514,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)
@@ -532,7 +558,7 @@
 This project includes the software: org.apache.commons
   Available at: http://commons.apache.org/
   Developed by: The Apache Software Foundation (http://www.apache.org/)
-  Version used: 3.1; 1.4
+  Version used: 3.3.2; 1.4
   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.felix
@@ -604,12 +630,24 @@
   Version used: 1.1
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 
+This project includes the software: org.codehaus.woodstox
+  Available at: http://wiki.fasterxml.com/WoodstoxStax2
+  Developed by: fasterxml.com (http://fasterxml.com)
+  Version used: 3.1.4
+  Used under the following license: BSD License (http://www.opensource.org/licenses/bsd-license.php)
+
 This project includes the software: org.eclipse.jetty
   Available at: http://www.eclipse.org/jetty
   Developed by: Webtide (http://webtide.com)
   Version used: 9.2.13.v20150730
   Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 
+This project includes the software: org.eclipse.jetty.toolchain
+  Available at: http://www.eclipse.org/jetty
+  Developed by: Mort Bay Consulting (http://www.mortbay.com)
+  Version used: 3.1.M0
+  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+
 This project includes the software: org.freemarker
   Available at: http://freemarker.org/
   Version used: 2.3.22
@@ -650,12 +688,6 @@
   Version used: 0.9.9-RC1
   Used under the following license: WTFPL (http://www.wtfpl.net/)
 
-This project includes the software: org.scala-lang
-  Available at: http://www.scala-lang.org/
-  Developed by: LAMP/EPFL (http://lamp.epfl.ch/)
-  Version used: 2.9.1-1
-  Used under the following license: BSD License (http://www.scala-lang.org/downloads/license.html)
-
 This project includes the software: org.slf4j
   Available at: http://www.slf4j.org
   Developed by: QOS.ch (http://www.qos.ch)
@@ -706,19 +738,11 @@
       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
+  Used under the following license: <no license info>
 
-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
-  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
+  Used under the following license: <no license info>
 
 This project includes the software: typeahead.js
   Available at: https://github.com/twitter/typeahead.js
diff --git a/brooklyn-dist/downstream-parent/pom.xml b/brooklyn-dist/downstream-parent/pom.xml
index 6a7d694..f97dba3 100644
--- a/brooklyn-dist/downstream-parent/pom.xml
+++ b/brooklyn-dist/downstream-parent/pom.xml
@@ -57,7 +57,7 @@
     <jclouds.groupId>org.apache.jclouds</jclouds.groupId> <!-- JCLOUDS_GROUPID_VERSION -->
 
     <!-- versions should match those used by Brooklyn, to avoid conflicts -->
-    <jclouds.version>1.9.1</jclouds.version> <!-- JCLOUDS_VERSION -->
+    <jclouds.version>1.9.2</jclouds.version> <!-- JCLOUDS_VERSION -->
     <logback.version>1.0.7</logback.version>
     <slf4j.version>1.6.6</slf4j.version>  <!-- used for java.util.logging jul-to-slf4j interception -->
     <guava.version>17.0</guava.version>
diff --git a/brooklyn-docs/guide/misc/release-notes.md b/brooklyn-docs/guide/misc/release-notes.md
index e67048e..3661ab1 100644
--- a/brooklyn-docs/guide/misc/release-notes.md
+++ b/brooklyn-docs/guide/misc/release-notes.md
@@ -36,6 +36,23 @@
 Classes such as HttpFeed that previously returned org.apache.brooklyn.util.core.http.HttpToolResponse in some methods now 
 return org.apache.brooklyn.util.HttpToolResponse.
 
+2. **Major:** Locations set in YAML or on a spec are no longer passed to `child.start(...)` by `AbstractApplication`;
+this has no effect in most cases as `SoftwareProcess.start` looks at local and inherited locations, but in ambiguous cases
+it means that locally defined locations are now preferred. Other classes of entities may need to do similar behaviour,
+and it means that calls to `Entity.getLocations()` in some cases will not show parent locations,
+unless discovered and set locally e.g. `start()`. The new method `Entities.getAllInheritedLocations(Entity)`
+can be used to traverse the hierarchy.  It also means that when a type in the registry (catalog) includes a location,
+and a caller references it, that location will now take priority over a location defined in a parent.
+Additionally, any locations specified in YAML extending the registered type will now *replace* locations on the referenced type;
+this means in many cases an explicit `locations: []` when extending a type will cause locations to be taken from the
+parent or application root in YAML. Related to this, tags from referencing specs now preceed tags in the referenced types,
+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`.
diff --git a/docs/guide/yaml/test/example_yaml/entities/infrastructuredeploymenttestcase-entity.yaml b/brooklyn-docs/guide/yaml/example_yaml/entities/infrastructuredeploymenttestcase-entity.yaml
similarity index 100%
rename from docs/guide/yaml/test/example_yaml/entities/infrastructuredeploymenttestcase-entity.yaml
rename to brooklyn-docs/guide/yaml/example_yaml/entities/infrastructuredeploymenttestcase-entity.yaml
diff --git a/docs/guide/yaml/test/example_yaml/entities/loopovergroupmembers-entity.yaml b/brooklyn-docs/guide/yaml/example_yaml/entities/loopovergroupmembers-entity.yaml
similarity index 100%
rename from docs/guide/yaml/test/example_yaml/entities/loopovergroupmembers-entity.yaml
rename to brooklyn-docs/guide/yaml/example_yaml/entities/loopovergroupmembers-entity.yaml
diff --git a/brooklyn-docs/guide/yaml/winrm/index.md b/brooklyn-docs/guide/yaml/winrm/index.md
index 959f88c..a787aa0 100644
--- a/brooklyn-docs/guide/yaml/winrm/index.md
+++ b/brooklyn-docs/guide/yaml/winrm/index.md
@@ -250,6 +250,16 @@
 
     install.command: $brooklyn:formatString("c:\\myscript.bat %s", component("db").attributeWhenReady("datastore.url"))
 
+### Powershell - Using Start-Process
+
+When you are invoking a command from a powershell script with `Start-Process` cmdlet,
+please use the `-Wait` and the `-PassThru` arguments.
+Example `Start-Process C:\mycommand -Wait -PassThru`
+
+Using `-Wait` guarantees that the script process and its children and thus the winrm session won't be terminated until it is finished.
+`-PassThru` Returns a process object for each process that the cmdlet started. By default, this cmdlet does not generate any output.
+See https://technet.microsoft.com/en-us/library/hh849848.aspx
+
 ### Rebooting
 
 Where a reboot is required as part of the entity setup, this can be configured using
@@ -378,6 +388,12 @@
 This could cause the WinRM connection attempts to timeout. The location configuration option 
 `waitForWinRmAvailable` defaults to `30m` (i.e. 30 minutes). This can be increased if required.
 
+Incorrectly prepared Windows template can cause the deployment to time-out expecting an interaction by the user.
+You can verify if this is the case by RDP to the deployment which is taking to much time to complete. 
+It is recommended to manually deploy a single VM for every newly created Windows template to verify that it can be
+used for unattended installations and it doesn't wait and/or require an input by the user.
+See [Windows template settings for an Unattended Installation](#windows-template-settings-for-an-unattended-installation) under Known Limitations below. 
+
 ### Windows log files
 
 Details of the commands executed, and their results, can be found in the Brooklyn log and in the Brooklyn 
@@ -499,3 +515,12 @@
 
 Blueprint authors are strongly encourages to explicitly specific directories for file
 uploads and in their Powershell scripts.
+
+### Windows template settings for an Unattended Installation
+
+Windows template needs certain configuration to be applied to prevent windows setup UI from being displayed.
+The default behavior is to display it if there are incorrect or empty settings. Showing Setup UI will prevent the proper
+deployment, because it will expect interaction by the user such as agreeing on the license agreement or some of the setup dialogs.
+
+Detailed instruction how to prepare an Unattended installation are provided at [https://technet.microsoft.com/en-us/library/cc722411%28v=ws.10%29.aspx](https://technet.microsoft.com/en-us/library/cc722411%28v=ws.10%29.aspx).
+
diff --git a/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java b/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
index 6306956..5003d4d 100644
--- a/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
+++ b/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
@@ -75,6 +75,9 @@
     @SetFromFlag("socketUid")
     StringAttributeSensorAndConfigKey SOCKET_UID = new StringAttributeSensorAndConfigKey(
             "mysql.socketUid", "Socket uid, for use in file /tmp/mysql.sock.<uid>.3306 (or randomly generated if not set)", null);
+
+    @SetFromFlag("generalLog")
+    ConfigKey GENERAL_LOG = ConfigKeys.newBooleanConfigKey("mysql.general_log", "Enable general log", false);
     
     /** @deprecated since 0.7.0 use DATASTORE_URL */ @Deprecated
     AttributeSensor<String> MYSQL_URL = DATASTORE_URL;
diff --git a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf
index 85f55ab..40b4086 100644
--- a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf
+++ b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf
@@ -13,7 +13,15 @@
 basedir         = ${driver.baseDir}
 datadir         = ${driver.dataDir}
 bind-address    = 0.0.0.0
+log_error       = ${driver.runDir}/mysql_error_${entity.getId()}.log
+log_warnings    = 2
+general_log      = ${config["mysql.general_log"]?string('on','off')}
+general_log_file = ${driver.runDir}/mysql_general_${entity.getId()}.log
 # skip-networking
 
+#Prevent the GRANT statement from automatically creating new user accounts if it would otherwise do so,
+#unless authentication information is specified
+sql_mode = NO_AUTO_CREATE_USER
+
 # Custom configuration options
 ${driver.mySqlServerOptionsString}
\ No newline at end of file
diff --git a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf
index 791f2da..54a773b 100644
--- a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf
+++ b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf
@@ -15,6 +15,10 @@
 bind-address    = 0.0.0.0
 # skip-networking
 
+#Prevent the GRANT statement from automatically creating new user accounts if it would otherwise do so,
+#unless authentication information is specified
+sql_mode = NO_AUTO_CREATE_USER
+
 # Replication config
 server-id       = 1
 binlog-format   = mixed
diff --git a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf
index 1c69423..b4af02a 100644
--- a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf
+++ b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf
@@ -16,6 +16,10 @@
 bind-address    = 0.0.0.0
 # skip-networking
 
+#Prevent the GRANT statement from automatically creating new user accounts if it would otherwise do so,
+#unless authentication information is specified
+sql_mode = NO_AUTO_CREATE_USER
+
 # Replication config
 server-id       = ${config["mysql.server_id"]}
 relay-log       = mysql-slave-${config["mysql.server_id"]}-relay
diff --git a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java
index 8f4e7fb..a95adf9 100644
--- a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java
+++ b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java
@@ -28,6 +28,7 @@
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.feed.ConfigToAttributes;
+import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.enricher.stock.Enrichers;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -104,11 +105,14 @@
     @Override
     public void start(Collection<? extends Location> locations) {
         if (isLegacyConstruction()) {
+            // TODO should no longer be needed?
             init();
         }
 
-        if (locations.isEmpty()) locations = getLocations();
+        locations = MutableList.copyOf(Locations.getLocationsCheckingAncestors(locations, this));
+
         Iterables.getOnlyElement(locations); // Assert just one
+        // set it; here we don't allow changing locations
         addLocations(locations);
 
         List<Entity> childrenToStart = MutableList.<Entity>of(getCluster());
diff --git a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java
index fc4eddf..874bed8 100644
--- a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java
+++ b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java
@@ -44,14 +44,19 @@
 public interface RabbitBroker extends SoftwareProcess, MessageBroker, AmqpServer {
 
     @SetFromFlag("version")
-    public static final ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "2.8.7");
+    public static final ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "3.6.0");
 
     @SetFromFlag("downloadUrl")
     public static final BasicAttributeSensorAndConfigKey<String> DOWNLOAD_URL = new BasicAttributeSensorAndConfigKey<String>(
-            SoftwareProcess.DOWNLOAD_URL, "http://www.rabbitmq.com/releases/rabbitmq-server/v${version}/rabbitmq-server-generic-unix-${version}.tar.gz");
+            SoftwareProcess.DOWNLOAD_URL, "http://www.rabbitmq.com/releases/rabbitmq-server/v${version}/rabbitmq-server-generic-unix-${version}.tar.xz");
     
     @SetFromFlag("erlangVersion")
-    public static final BasicConfigKey<String> ERLANG_VERSION = new BasicConfigKey<String>(String.class, "erlang.version", "Erlang runtime version", "R15B");
+    public static final BasicConfigKey<String> ERLANG_VERSION = new BasicConfigKey<String>(String.class, "erlang.version", "Erlang runtime version", "18.2");
+
+    @SetFromFlag("erlangDebRepoUrl")
+    public static final BasicConfigKey<String> ERLANG_DEB_REPO_URL = new BasicConfigKey<String>(String.class, "erlang.deb.repo.url", 
+            "Deb file used to configure an external Erlang repository which provides up to date packages for Ubuntu/Debian", 
+            "http://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb");
 
     @SetFromFlag("rabbitmqConfigTemplateUrl")
     ConfigKey<String> CONFIG_TEMPLATE_URL = ConfigKeys.newStringConfigKey(
diff --git a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java
index c8d0a89..ad04d55 100644
--- a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java
+++ b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java
@@ -27,7 +27,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
@@ -39,6 +38,7 @@
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.net.Networking;
 import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.text.Strings;
 
 /**
  * TODO javadoc
@@ -81,40 +81,32 @@
     public void install() {
         List<String> urls = resolver.getTargets();
         String saveAs = resolver.getFilename();
-        // Version and architecture are only required for download of epel package on RHEL/Centos systems so pick sensible
-        // defaults if unavailable
-        String osMajorVersion = getMachine().getOsDetails().getVersion();
-        if (Strings.isNullOrEmpty(osMajorVersion)) {
-            osMajorVersion = "7";
-        } else {
-            osMajorVersion = osMajorVersion.indexOf(".") > 0 ? osMajorVersion.substring(0, osMajorVersion.indexOf('.')) : osMajorVersion;
-            if (!CENTOS_VERSION_TO_EPEL_VERSION.keySet().contains(osMajorVersion)) {
-                osMajorVersion = "7";
-            }
-        }
-        String epelVersion = CENTOS_VERSION_TO_EPEL_VERSION.get(osMajorVersion);
-        String osArchitecture = getMachine().getOsDetails().getArch();
-        if (Strings.isNullOrEmpty(osArchitecture)) {
-            osArchitecture = "x86_64";
-        }
+
 
         List<String> commands = ImmutableList.<String>builder()
-                // EPEL repository for erlang install required on some Centos distributions
-                .add(chainGroup("which yum", sudo("yum -y update ca-certificates"), sudo("rpm -Uvh --replacepkgs " +
-                        format("http://download.fedoraproject.org/pub/epel/%s/%s/epel-release-%s.noarch.rpm", osMajorVersion, osArchitecture, epelVersion))))
-                .add(ifExecutableElse0("zypper", chainGroup(
-                        ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/SLE_11_SP3 erlang_sles_11")),
-                        ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_11.4 erlang_suse_11")),
-                        ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_12.3 erlang_suse_12")),
-                        ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_13.1 erlang_suse_13")))))
+                // RabbitMQ recommends Erlang 18. Release notes state:
+                // ====
+                // Minimum required Erlang version is R16B03 for plain ("just TCP") connections for all protocols 
+                // and 17.5 for TLS ones (18.x is recommended for both).
+                // ====
+                //
+                // The recommended provider for up to date Erlang versions is Erlang Solutions now.
+                // Supported platforms by Erlang Solutions are: CentOS, RHEL, Ubuntu and Debian.
+                // 
+                // New Erlang versions for SUSE are provided via the openSUSE repositories
+                // 
+                // EPEL 6 provides only packages for Erlang 14, but EPEL 7 - Erlang 16
+                // 
+                .add(ifExecutableElse0("apt-get", getAptRepository()))
+                .add(ifExecutableElse0("yum", getYumRepository()))
+                .add(ifExecutableElse0("zypper", getZypperRepository()))
                 .add(installPackage( // NOTE only 'port' states the version of Erlang used, maybe remove this constraint?
                         ImmutableMap.of(
-                                "apt", "erlang-nox erlang-dev",
                                 "port", "erlang@"+getErlangVersion()+"+ssl"),
-                        "erlang"))
+                                "erlang"))
                 .addAll(commandsToDownloadUrlsAs(urls, saveAs))
                 .add(installExecutable("tar"))
-                .add(format("tar xvzf %s",saveAs))
+                .add(format("tar xvf %s",saveAs))
                 .build();
 
         newScript(INSTALLING).
@@ -147,9 +139,9 @@
         newScript(MutableMap.of("usePidFile", false), LAUNCHING)
             .body.append(
                 "nohup ./sbin/rabbitmq-server > console-out.log 2> console-err.log &",
-                "for i in {1..30}\n" +
+                "for i in {1..60}\n" +
                     "do\n" +
-                     "    grep 'broker running' console-out.log && exit\n" +
+                     "    grep 'Starting broker... completed' console-out.log && exit\n" +
                      "    sleep 1\n" +
                      "done",
                 "echo \"Couldn't determine if rabbitmq-server is running\"",
@@ -166,7 +158,6 @@
             ).execute();
     }
 
-
     public String getPidFile() { return "rabbitmq.pid"; }
 
     @Override
@@ -183,7 +174,6 @@
                 .execute();
     }
 
-
     @Override
     public void kill() {
         stop(); // TODO No pid file to easily do `kill -9`
@@ -205,4 +195,69 @@
     private String getConfigPath() {
         return getRunDir() + "/rabbitmq";
     }
+
+    private String getYumRepository() {
+        String yumRepoFileName = "erlang_solutions.repo";
+        
+        // Version and architecture are only required for download of epel package on RHEL/Centos systems so pick sensible
+        // defaults if unavailable
+        //
+        // EPEL is still required as it is a prerequisite for the packages provided by Erlang Solutions
+        
+        String osMajorVersion = getMachine().getOsDetails().getVersion();
+        if (Strings.isBlank(osMajorVersion)) {
+            osMajorVersion = "7";
+        } else {
+            osMajorVersion = osMajorVersion.indexOf(".") > 0 ? osMajorVersion.substring(0, osMajorVersion.indexOf('.')) : osMajorVersion;
+            if (!CENTOS_VERSION_TO_EPEL_VERSION.keySet().contains(osMajorVersion)) {
+                osMajorVersion = "7";
+            }
+        }
+        String epelVersion = CENTOS_VERSION_TO_EPEL_VERSION.get(osMajorVersion);
+        String osArchitecture = getMachine().getOsDetails().getArch();
+        if (Strings.isBlank(osArchitecture)) {
+            osArchitecture = "x86_64";
+        }
+
+        // Erlang Solutions provide separate packages for RHEL and CentOS, but they are hosted in a single repo.
+        // E.g. - http://packages.erlang-solutions.com/rpm/centos/6/x86_64/
+        // 
+        // Erlang Solutions created a repo configuration RPM which can NOT be used on RedHat, because it has explicit checks for CentOS.
+        // 
+        // Bellow we are creating a repo file that works for CentOS and RedHat
+        return chainGroup(
+                sudo("yum -y update ca-certificates"),
+                sudo("rpm -Uvh --replacepkgs " + format("http://download.fedoraproject.org/pub/epel/%s/%s/epel-release-%s.noarch.rpm", osMajorVersion, osArchitecture, epelVersion)),
+                "( cat << 'EOF_BROOKLYN'\n"
+                   + "[erlang-solutions]\n"
+                   + "name=Centos / RHEL " + osMajorVersion + " - $basearch - Erlang Solutions\n"
+                   + "baseurl=http://packages.erlang-solutions.com/rpm/centos/" + osMajorVersion + "/$basearch\n"
+                   + "gpgcheck=0\n"
+                   + "gpgkey=http://packages.erlang-solutions.com/debian/erlang_solutions.asc\n"
+                   + "enabled=1\n"
+                   + "EOF_BROOKLYN\n"
+                   + ") > " + yumRepoFileName,
+                  sudo(format("mv %s /etc/yum.repos.d/", yumRepoFileName)),
+                  sudo(format("chown root:root /etc/yum.repos.d/%s", yumRepoFileName))
+            );
+    }
+
+    private String getAptRepository() {
+        String debFileName = "erlang-repo.deb";
+
+        return chainGroup(
+                INSTALL_WGET,
+                format("wget --quiet -O %s %s", debFileName, entity.getConfig(RabbitBroker.ERLANG_DEB_REPO_URL)),
+                sudo(format("dpkg -i %s", debFileName))
+            );
+    }
+
+    private String getZypperRepository() {
+        return chainGroup(
+                ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/SLE_11_SP3 erlang_sles_11")),
+                ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_11.4 erlang_suse_11")),
+                ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_12.3 erlang_suse_12")),
+                ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_13.1 erlang_suse_13")));
+    }
 }
+
diff --git a/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config b/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config
index b4428f0..c350b65 100644
--- a/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config
+++ b/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config
@@ -1,5 +1,6 @@
 [
 <#if entity.enableManagementPlugin>
+    {rabbit, [{loopback_users, []}]},
     {rabbitmq_mochiweb, [{listeners, [{mgmt, [{port, ${entity.managementPort?c}}]}]}]}
 </#if>
 ].
\ No newline at end of file
diff --git a/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java b/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
index 78898bd..8c52746 100644
--- a/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
+++ b/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
@@ -38,6 +38,7 @@
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.entity.trait.StartableMethods;
 import org.apache.brooklyn.core.feed.ConfigToAttributes;
+import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.enricher.stock.Enrichers;
 import org.apache.brooklyn.entity.group.DynamicGroupImpl;
 import org.apache.brooklyn.entity.proxy.LoadBalancer;
@@ -207,7 +208,8 @@
                 init();
             }
 
-            if (locations.isEmpty()) locations = getLocations();
+            locations = Locations.getLocationsCheckingAncestors(locations, this);
+            // store inherited locations
             addLocations(locations);
 
             LoadBalancer loadBalancer = getController();
@@ -229,7 +231,8 @@
                 }
             }
 
-            Entities.invokeEffectorList(this, childrenToStart, Startable.START, ImmutableMap.of("locations", locations)).get();
+            // don't propagate start locations
+            Entities.invokeEffectorList(this, childrenToStart, Startable.START, ImmutableMap.of("locations", MutableList.of())).get();
             if (startControllerTask != null) {
                 startControllerTask.get();
             }
diff --git a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java
index 7440221..58cf946 100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java
@@ -374,6 +374,13 @@
         return this;
     }
     
+    /** clears locations defined in the spec */
+    public <V> EntitySpec<T> clearLocations() {
+        checkMutable();
+        locations.clear();
+        return this;        
+    }
+    
     /** adds the supplied locations to the spec */
     public <V> EntitySpec<T> locations(Iterable<? extends Location> val) {
         checkMutable();
diff --git a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
index ab046d5..789d282 100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
@@ -38,8 +38,10 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
@@ -81,7 +83,7 @@
 
     @Override
     public String toString() {
-        return Objects.toStringHelper(this).add("type", getType()).toString();
+        return Objects.toStringHelper(this).add("type", getType()).toString()+"@"+Integer.toHexString(System.identityHashCode(this));
     }
 
     protected abstract void checkValidType(Class<? extends T> type);
@@ -95,6 +97,19 @@
         catalogItemId = val;
         return self();
     }
+    // TODO in many places (callers to this method) we prefer a wrapper item ID;
+    // that is right, because the wrapper's defn will refer to the wrapped,
+    // but we might need also to collect the item ID's so that *all* can be searched.
+    // e.g. if R3 references R2 which references R1 any one of these might supply config keys 
+    // referencing resources or types in their local bundles. 
+    @Beta
+    public SpecT catalogItemIdIfNotNull(String val) {
+        if (val!=null) {
+            catalogItemId = val;
+        }
+        return self();
+    }
+
     
     public SpecT tag(Object tag) {
         tags.add(tag);
@@ -103,12 +118,49 @@
 
     /** adds the given tags */
     public SpecT tags(Iterable<Object> tagsToAdd) {
+        return tagsAdd(tagsToAdd);
+    }
+    /** adds the given tags */
+    public SpecT tagsAdd(Iterable<Object> tagsToAdd) {
         Iterables.addAll(this.tags, tagsToAdd);
         return self();
     }
+    /** replaces tags with the given */
+    public SpecT tagsReplace(Iterable<Object> tagsToReplace) {
+        this.tags.clear();
+        Iterables.addAll(this.tags, tagsToReplace);
+        return self();
+    }
     
+    // TODO which semantics are correct? replace has been the behaviour;
+    // add breaks tests and adds unwanted parameters,
+    // but replacing will cause some desired parameters to be lost.
+    // i (AH) think ideally the caller should remove any parameters which
+    // have been defined as config keys, and then add the others;
+    // or actually we should always add, since this is really defining the config keys,
+    // and maybe extend the SpecParameter object to be able to advertise whether
+    // it is a CatalogConfig or merely a config key, maybe introducing displayable, or even priority 
+    // (but note part of the reason for CatalogConfig.priority is that java reflection doesn't preserve field order) .
+    // see also comments on the camp SpecParameterResolver.
+    @Beta
     public SpecT parameters(List<? extends SpecParameter<?>> parameters) {
-        this.parameters = ImmutableList.copyOf(checkNotNull(parameters, "parameters"));
+        return parametersReplace(parameters);
+    }
+    /** adds the given parameters */
+    @Beta
+    public SpecT parametersAdd(List<? extends SpecParameter<?>> parameters) {
+        // parameters follows immutable pattern, unlike the other fields
+        Builder<SpecParameter<?>> result = ImmutableList.<SpecParameter<?>>builder();
+        if (this.parameters!=null)
+            result.addAll(this.parameters);
+        result.addAll( checkNotNull(parameters, "parameters") );
+        this.parameters = result.build();
+        return self();
+    }
+    /** replaces parameters with the given */
+    @Beta
+    public SpecT parametersReplace(List<? extends SpecParameter<?>> parameters) {
+        this.parameters = ImmutableList.copyOf( checkNotNull(parameters, "parameters") );
         return self();
     }
 
diff --git a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/mgmt/Task.java b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/mgmt/Task.java
index c8f1c00..42147c5 100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/mgmt/Task.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/mgmt/Task.java
@@ -81,6 +81,24 @@
     public boolean isError();
 
     /**
+     * As {@link Future#isDone()}. In particular if cancelled, this will return true
+     * as soon as it is cancelled. The thread for this task may still be running,
+     * if the cancellation (often an interruption, but may be weaker) has not applied,
+     * and submitted threads may also be running depending on cancellation parameters.
+     * <p>
+     * {@link #get()} is guaranteed to return immediately, throwing in the case of cancellation
+     * prior to completion (and including the case above where a thread may still be running).
+     * <p>
+     * To check whether cancelled threads for this task have completed, 
+     * inspect {@link #getEndTimeUtc()}, which is guaranteed to be set when threads complete
+     * if the thread is started (as determinable by whether {@link #getStartTimeUtc()} is set).
+     * (The threads of submitted/child tasks will usually be independent; to determine their
+     * completion requires inspecting the {@link ExecutionManager}.)  
+     */
+    @Override
+    public boolean isDone();
+    
+    /**
      * Causes calling thread to block until the task is started.
      */
     public void blockUntilStarted();
diff --git a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/AbstractResource.java b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/AbstractResource.java
index 56a51f4..c24eab1 100644
--- a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/AbstractResource.java
+++ b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/AbstractResource.java
@@ -30,7 +30,6 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 
 /** Superclass of CAMP resource implementation objects.
  * Typically used to hold common state of implementation objects
@@ -91,7 +90,7 @@
         return representationSkew;
     }
     public Map<String, Object> getCustomAttributes() {
-        return ImmutableMap.copyOf(customAttributes);
+        return MutableMap.copyOf(customAttributes).asUnmodifiable();
     }
     
     // setters
diff --git a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/pdp/DeploymentPlan.java b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/pdp/DeploymentPlan.java
index 792392e..7ad3164 100644
--- a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/pdp/DeploymentPlan.java
+++ b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/pdp/DeploymentPlan.java
@@ -22,13 +22,11 @@
 import java.util.List;
 import java.util.Map;
 
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
 public class DeploymentPlan {
 
     String name;
@@ -107,15 +105,15 @@
     }
     
     public List<Artifact> getArtifacts() {
-        return ImmutableList.copyOf(artifacts);
+        return MutableList.copyOf(artifacts).asUnmodifiable();
     }
 
     public List<Service> getServices() {
-        return ImmutableList.copyOf(services);
+        return MutableList.copyOf(services).asUnmodifiable();
     }
 
     public Map<String, Object> getCustomAttributes() {
-        return ImmutableMap.copyOf(customAttributes);
+        return MutableMap.copyOf(customAttributes).asUnmodifiable();
     }
 
     /**
diff --git a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java
index 26822aa..f45d7c6 100644
--- a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java
+++ b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java
@@ -22,9 +22,9 @@
 import java.util.Map;
 
 import org.apache.brooklyn.camp.spi.resolve.PlanInterpreter;
+import org.apache.brooklyn.util.collections.MutableMap;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 
 public class PlanInterpretationContext {
 
@@ -34,7 +34,7 @@
 
     public PlanInterpretationContext(Map<String,?> originalDeploymentPlan, List<PlanInterpreter> interpreters) {
         super();
-        this.originalDeploymentPlan = ImmutableMap.copyOf(originalDeploymentPlan);
+        this.originalDeploymentPlan = MutableMap.copyOf(originalDeploymentPlan).asUnmodifiable();
         this.interpreters = ImmutableList.copyOf(interpreters);
         this.allInterpreter = new PlanInterpreter() {
             @Override
diff --git a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationNode.java b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationNode.java
index 6542b8b..2cf3d5d 100644
--- a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationNode.java
+++ b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationNode.java
@@ -27,8 +27,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 
 /** Helper class for {@link PlanInterpreter} instances, doing the recursive work */
@@ -228,7 +226,7 @@
 
     public void immutable() {
         if (!isChanged()) {
-            if (!testCollectionImmutable(getNewValue())) {
+            if (!checkCollectionImmutable(getNewValue())) {
                 // results of Yaml parse are not typically immutable,
                 // so force them to be changed so result of interpretation is immutable
                 changed = true;
@@ -242,19 +240,19 @@
     }
     
     private void checkImmutable(Object in) {
-        if (!testCollectionImmutable(in))
+        if (!checkCollectionImmutable(in))
             log.warn("Node original value "+in+" at "+this+" should be immutable");
     }
     
-    private static boolean testCollectionImmutable(Object in) {
-        if (in instanceof Map) return (in instanceof ImmutableMap);
-        if (in instanceof Iterable) return (in instanceof ImmutableList);
+    private static boolean checkCollectionImmutable(Object in) {
+        // not used -- input might now be UnmodifiableMap, as some args might be null, 
+        // and UnmodifiableMap is private :(  ... (and same for list)
         return true;
     }
 
     private static Object immutable(Object in) {
-        if (in instanceof Map) return ImmutableMap.copyOf((Map<?,?>)in);
-        if (in instanceof Iterable) return ImmutableList.copyOf((Iterable<?>)in);
+        if (in instanceof Map) return MutableMap.copyOf((Map<?,?>)in).asUnmodifiable();
+        if (in instanceof Iterable) return MutableList.copyOf((Iterable<?>)in).asUnmodifiable();
         return in;
     }
 
diff --git a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
index 5840440..931c2e7 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
@@ -89,20 +89,20 @@
         // first build the children into an empty shell app
         List<EntitySpec<?>> childSpecs = createServiceSpecs(template, platform, loader, encounteredTypeSymbolicNames);
         for (EntitySpec<?> childSpec : childSpecs) {
-            app.child(childSpec);
+            // children get parsed and unwrapped irrespective of the NEVER_UNWRAP_APPS setting;
+            // we could support a NEVER_UNWRAP_NESTED_ENTITIES item but i don't know if there's a use case
+            app.child(EntityManagementUtils.unwrapEntity(childSpec));
         }
 
-        if (shouldUnwrap(template, app)) {
+        if (allowedToUnwrap(template, app)) {
             app = EntityManagementUtils.unwrapApplication(app);
         }
 
         return app;
     }
 
-    private boolean shouldUnwrap(AssemblyTemplate template, EntitySpec<? extends Application> app) {
-        if (Boolean.TRUE.equals(TypeCoercions.coerce(template.getCustomAttributes().get(NEVER_UNWRAP_APPS_PROPERTY), Boolean.class)))
-            return false;
-        return EntityManagementUtils.canPromoteWrappedApplication(app);
+    private boolean allowedToUnwrap(AssemblyTemplate template, EntitySpec<? extends Application> app) {
+        return !(Boolean.TRUE.equals(TypeCoercions.coerce(template.getCustomAttributes().get(NEVER_UNWRAP_APPS_PROPERTY), Boolean.class)));
     }
 
     private List<EntitySpec<?>> buildTemplateServicesAsSpecs(BrooklynClassLoadingContext loader, AssemblyTemplate template, CampPlatform platform, Set<String> encounteredRegisteredTypeIds) {
diff --git a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index 8f4db88..fdc2559 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -49,6 +49,7 @@
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
 import org.apache.brooklyn.core.mgmt.ManagementContextInjectable;
 import org.apache.brooklyn.core.mgmt.classloading.JavaBrooklynClassLoadingContext;
 import org.apache.brooklyn.core.resolve.entity.EntitySpecResolver;
@@ -168,6 +169,7 @@
             }
             throw new IllegalStateException("Unable to create spec for type " + type + ". " + msgDetails);
         }
+        spec = EntityManagementUtils.unwrapEntity(spec);
 
         populateSpec(spec, encounteredRegisteredTypeSymbolicNames);
 
@@ -207,12 +209,13 @@
                 // encounteredRegisteredTypeIds must contain the items currently being loaded (the dependency chain),
                 // but not parent items in this type already resolved.
                 EntitySpec<? extends Entity> childSpec = entityResolver.resolveSpec(encounteredRegisteredTypeIds);
-                spec.child(childSpec);
+                spec.child(EntityManagementUtils.unwrapEntity(childSpec));
             }
         }
 
-        if (source!=null)
+        if (source!=null) {
             spec.tag(BrooklynTags.newYamlSpecTag(source));
+        }
 
         if (!Strings.isBlank(name))
             spec.displayName(name);
@@ -221,9 +224,13 @@
         if (planId != null)
             spec.configure(BrooklynCampConstants.PLAN_ID, planId);
 
-        List<Location> childLocations = new BrooklynYamlLocationResolver(mgmt).resolveLocations(attrs.getAllConfig(), true);
-        if (childLocations != null)
-            spec.locations(childLocations);
+        List<Location> locations = new BrooklynYamlLocationResolver(mgmt).resolveLocations(attrs.getAllConfig(), true);
+        if (locations != null) {
+            // override locations defined in the type if locations are specified here
+            // empty list can be used by caller to clear, so they are inherited
+            spec.clearLocations();
+            spec.locations(locations);
+        }
 
         decorateSpec(spec, encounteredRegisteredTypeIds);
     }
@@ -326,7 +333,7 @@
             if (input instanceof Map)
                 return transformSpecialFlags((Map<?, ?>)input);
             else if (input instanceof Set<?>)
-                return MutableSet.of(transformSpecialFlags((Iterable<?>)input));
+                return MutableSet.copyOf(transformSpecialFlags((Iterable<?>)input));
             else if (input instanceof List<?>)
                 return MutableList.copyOf(transformSpecialFlags((Iterable<?>)input));
             else if (input instanceof Iterable<?>)
@@ -365,7 +372,9 @@
                 @SuppressWarnings("unchecked")
                 Map<String, Object> resolvedConfig = (Map<String, Object>)transformSpecialFlags(specConfig.getSpecConfiguration());
                 specConfig.setSpecConfiguration(resolvedConfig);
-                return Factory.newInstance(getLoader(), specConfig.getSpecConfiguration()).resolveSpec(encounteredRegisteredTypeIds);
+                EntitySpec<?> entitySpec = Factory.newInstance(getLoader(), specConfig.getSpecConfiguration()).resolveSpec(encounteredRegisteredTypeIds);
+                
+                return EntityManagementUtils.unwrapEntity(entitySpec);
             }
             if (flag instanceof ManagementContextInjectable) {
                 log.debug("Injecting Brooklyn management context info object: {}", flag);
diff --git a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
index 6734875..4913cb1 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
@@ -186,6 +186,9 @@
         @Override
         public void decorate(EntitySpec<?> entitySpec, ConfigBag attrs) {
             List<? extends SpecParameter<?>> explicitParams = buildListOfTheseDecorationsFromEntityAttributes(attrs);
+            // TODO see discussion at EntitySpec.parameters; 
+            // maybe we should instead inherit always, or 
+            // inherit except where it is set as config and then add the new explicit ones
             if (!explicitParams.isEmpty()) {
                 entitySpec.parameters(explicitParams);
             }
diff --git a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java
index 602baa3..5639945 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java
@@ -20,7 +20,6 @@
 
 import java.util.Set;
 
-import com.google.common.collect.Iterables;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
@@ -115,7 +114,7 @@
             throw new IllegalStateException("Creating spec from "+item+", got "+spec.getType()+" which is incompatible with expected "+expectedType);                
         }
 
-        ((AbstractBrooklynObjectSpec<?, ?>)spec).catalogItemId(item.getId());
+        ((AbstractBrooklynObjectSpec<?, ?>)spec).catalogItemIdIfNotNull(item.getId());
 
         if (Strings.isBlank( ((AbstractBrooklynObjectSpec<?, ?>)spec).getDisplayName() ))
             ((AbstractBrooklynObjectSpec<?, ?>)spec).displayName(item.getDisplayName());
@@ -131,15 +130,16 @@
         if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
             EntitySpec<? extends Application> appSpec = ((AssemblyTemplateSpecInstantiator)instantiator).createApplicationSpec(at, camp, loader, encounteredTypes);
 
-            if (!isApplication && EntityManagementUtils.canPromoteChildrenInWrappedApplication(appSpec)) {
-                EntitySpec<?> childSpec = Iterables.getOnlyElement(appSpec.getChildren());
-                EntityManagementUtils.mergeWrapperParentSpecToChildEntity(appSpec, childSpec);
-                return childSpec;
-            }
+            // above will unwrap but only if it's an Application (and it's permitted); 
+            // but it doesn't know whether we need an App or if an Entity is okay  
+            if (!isApplication) return EntityManagementUtils.unwrapEntity(appSpec);
+            // if we need an App then definitely *don't* unwrap here because
+            // the instantiator will have done that, and it knows if the plan
+            // specified a wrapped app explicitly (whereas we don't easily know that here!)
             return appSpec;
             
         } else {
-            throw new IllegalStateException("Unable to instantiate YAML; incompatible instantiator "+instantiator+" for "+at);
+            throw new IllegalStateException("Unable to instantiate YAML; invalid type or parameters in plan:\n"+plan);
         }
 
     }
diff --git a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
index a417e32..48a0283 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
@@ -115,7 +115,7 @@
         
         try {
             if (log.isDebugEnabled())
-                log.debug("Queuing task to resolve "+dsl);
+                log.debug("Queuing task to resolve "+dsl+", called by "+Tasks.current());
 
             EntityInternal entity = (EntityInternal) BrooklynTaskTags.getTargetOrContextEntity(Tasks.current());
             ExecutionContext exec =
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
index 909564c..4478f2b 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
@@ -20,6 +20,7 @@
 
 import java.io.Reader;
 import java.io.StringReader;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.brooklyn.api.catalog.BrooklynCatalog;
@@ -115,11 +116,14 @@
     }
 
     protected Entity createAndStartApplication(String input) throws Exception {
+        return createAndStartApplication(input, MutableMap.<String,String>of());
+    }
+    protected Entity createAndStartApplication(String input, Map<String,String> startParameters) throws Exception {
         EntitySpec<?> spec = 
             mgmt().getTypeRegistry().createSpecFromPlan(CampTypePlanTransformer.FORMAT, input, RegisteredTypeLoadingContexts.spec(Application.class), EntitySpec.class);
         final Entity app = brooklynMgmt.getEntityManager().createEntity(spec);
         // start the app (happens automatically if we use camp to instantiate, but not if we use crate spec approach)
-        app.invoke(Startable.START, MutableMap.<String,String>of()).get();
+        app.invoke(Startable.START, startParameters).get();
         return app;
     }
 
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptySoftwareProcessYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptySoftwareProcessYamlTest.java
index bb3eeb9..0554917 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptySoftwareProcessYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptySoftwareProcessYamlTest.java
@@ -23,14 +23,20 @@
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
 import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityAsserts;
 import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.util.collections.Jsonya;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.Test;
-import org.apache.brooklyn.location.ssh.SshMachineLocation;
-import org.apache.brooklyn.util.collections.Jsonya;
+
+import com.google.common.collect.Iterables;
 
 @Test
 public class EmptySoftwareProcessYamlTest extends AbstractYamlTest {
@@ -97,6 +103,22 @@
         Location actualMachine = entityLocationIterator.next();
         Assert.assertTrue(actualMachine instanceof SshMachineLocation, "wrong location: "+actualMachine);
         // TODO this, below, probably should be 'localhost on entity', see #1377
-        Assert.assertEquals(actualMachine.getParent().getDisplayName(), "loopback on app");
+        Assert.assertEquals(actualMachine.getParent().getDisplayName(), "localhost on entity");
+    }
+    
+    @Test(groups="Integration")
+    public void testNoSshing() throws Exception {
+        Entity app = createAndStartApplication(
+                "location: byon:(hosts=\"1.2.3.4\")",
+                "services:",
+                "- type: "+EmptySoftwareProcess.class.getName(),
+                "  brooklyn.config:",
+                "    sshMonitoring.enabled: false",
+                "    "+BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION.getName()+": true");
+        waitForApplicationTasks(app);
+
+        EmptySoftwareProcess entity = Iterables.getOnlyElement(Entities.descendants(app, EmptySoftwareProcess.class));
+        EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true);
+        EntityAsserts.assertAttributeEqualsContinually(entity, Attributes.SERVICE_UP, true);
     }
 }
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptyWindowsProcessYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptyWindowsProcessYamlTest.java
new file mode 100644
index 0000000..77043c7
--- /dev/null
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptyWindowsProcessYamlTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.camp.brooklyn;
+
+import org.apache.brooklyn.api.entity.Entity;
+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.entity.software.base.EmptyWindowsProcess;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+
+@Test
+public class EmptyWindowsProcessYamlTest extends AbstractYamlTest {
+
+    @Test(groups="Integration")
+    public void testNoWinrm() throws Exception {
+        Entity app = createAndStartApplication(
+                "location: byon:(hosts=\"1.2.3.4\",osFamily=windows)",
+                "services:",
+                "- type: "+EmptyWindowsProcess.class.getName(),
+                "  brooklyn.config:",
+                "    winrmMonitoring.enabled: false");
+        waitForApplicationTasks(app);
+
+        EmptyWindowsProcess entity = Iterables.getOnlyElement(Entities.descendants(app, EmptyWindowsProcess.class));
+        EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true);
+        EntityAsserts.assertAttributeEqualsContinually(entity, Attributes.SERVICE_UP, true);
+        
+        Iterables.find(entity.getLocations(), Predicates.instanceOf(WinRmMachineLocation.class));
+    }
+}
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
index 8131208..337c302 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
@@ -53,6 +53,7 @@
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.core.test.entity.TestEntityImpl;
 import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.group.DynamicFabric;
 import org.apache.brooklyn.entity.software.base.SameServerEntity;
 import org.apache.brooklyn.entity.stock.BasicEntity;
 import org.apache.brooklyn.util.collections.MutableMap;
@@ -60,6 +61,8 @@
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Functionals;
 import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -67,6 +70,7 @@
 import org.testng.annotations.Test;
 import org.testng.collections.Lists;
 
+import com.google.common.base.Joiner;
 import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -100,7 +104,6 @@
         setupAndCheckTestEntityInBasicYamlWith();
     }
 
-    @SuppressWarnings("unchecked")
     @Test
     public void testBrooklynConfig() throws Exception {
         Entity testEntity = setupAndCheckTestEntityInBasicYamlWith( 
@@ -113,6 +116,17 @@
             "      - dogs",
             "      - cats",
             "      - badgers",
+            "    test.confSetPlain: !!set",
+            "      ? square",
+            "      ? circle",
+            "      ? triangle",
+            "    test.confMapThing:",
+            "      foo: bar",
+            "      baz: qux",
+            "    test.confListThing:",
+            "      - dogs",
+            "      - cats",
+            "      - badgers",
             "    test.confSetThing: !!set",
             "      ? square",
             "      ? circle",
@@ -120,14 +134,13 @@
             "    test.confObject: 5");
         
         Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_NAME), "Test Entity Name");
-        List<String> list = testEntity.getConfig(TestEntity.CONF_LIST_PLAIN);
-        Assert.assertEquals(list, ImmutableList.of("dogs", "cats", "badgers"));
-        Map<String, String> map = testEntity.getConfig(TestEntity.CONF_MAP_PLAIN);
-        Assert.assertEquals(map, ImmutableMap.of("foo", "bar", "baz", "qux"));
-        Set<String> set = (Set<String>)testEntity.getConfig(TestEntity.CONF_SET_THING);
-        Assert.assertEquals(set, ImmutableSet.of("square", "circle", "triangle"));
-        Object object = testEntity.getConfig(TestEntity.CONF_OBJECT);
-        Assert.assertEquals(object, 5);
+        Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_OBJECT), 5);
+        Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_LIST_PLAIN), ImmutableList.of("dogs", "cats", "badgers"));
+        Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_MAP_PLAIN), ImmutableMap.of("foo", "bar", "baz", "qux"));
+        Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_SET_PLAIN), ImmutableSet.of("square", "circle", "triangle"));
+        Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_LIST_THING), ImmutableList.of("dogs", "cats", "badgers"));
+        Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_MAP_THING), ImmutableMap.of("foo", "bar", "baz", "qux"));
+        Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_SET_THING), ImmutableSet.of("square", "circle", "triangle"));
     }
 
     @Test
@@ -592,7 +605,7 @@
         Assert.assertEquals(app.getChildren().size(), 1);
         Entity entity = app.getChildren().iterator().next();
         Assert.assertNotNull(entity);
-        Assert.assertEquals(entity.getLocations().size(), 1);
+        Assert.assertEquals(entity.getLocations().size(), 0);
     }
 
     @Test
@@ -631,7 +644,8 @@
         Assert.assertEquals(app.getChildren().size(), 1);
         Entity entity = app.getChildren().iterator().next();
         Assert.assertNotNull(entity);
-        Assert.assertEquals(entity.getLocations().size(), 2);
+        // 2016-01 locations now not set on entity unless explicitly passed to "start" 
+        Assert.assertEquals(entity.getLocations().size(), 0);
     }
 
     @Test
@@ -666,6 +680,26 @@
         Assert.assertEquals(app.getChildren().size(), 1);
         Entity entity = app.getChildren().iterator().next();
         
+        Assert.assertEquals(entity.getLocations().size(), 1);
+        Iterator<Location> entityLocationIterator = entity.getLocations().iterator();
+        Assert.assertEquals(entityLocationIterator.next().getDisplayName(), "localhost name");
+        
+        Location appLocation = app.getLocations().iterator().next();
+        Assert.assertEquals(appLocation.getDisplayName(), "byon name");
+    }
+
+    @Test
+    public void testWithEntityLocationsAndStartInLocation() throws Exception {
+        Entity app = createAndStartApplication(Streams.readFully(loadYaml("test-entity-basic-template.yaml",  
+            "  location: localhost:(name=localhost name)")),
+            // must pass as JSON list because otherwise the comma confuses the list parser
+            MutableMap.of("locations", "[ "+JavaStringEscapes.wrapJavaString(
+                "byon:(hosts=\"1.1.1.1\", name=\"byon name\")")+" ]") );
+        waitForApplicationTasks(app);
+        Assert.assertEquals(app.getLocations().size(), 1);
+        Assert.assertEquals(app.getChildren().size(), 1);
+        Entity entity = app.getChildren().iterator().next();
+        
         Assert.assertEquals(entity.getLocations().size(), 2);
         Iterator<Location> entityLocationIterator = entity.getLocations().iterator();
         Assert.assertEquals(entityLocationIterator.next().getDisplayName(), "localhost name");
@@ -694,6 +728,48 @@
     }
 
     @Test
+    public void testCreateFabricWithLocationsAtTopLevel() throws Exception {
+        String yaml = Joiner.on("\n").join(
+                "services:",
+                "- type: org.apache.brooklyn.entity.group.DynamicFabric",
+                "  memberSpec:",
+                "    $brooklyn:entitySpec:",
+                "      type: org.apache.brooklyn.core.test.entity.TestEntity",
+                "locations:",
+                "- byon(hosts=\"1.1.1.1\")",
+                "- byon(hosts=\"1.1.1.2\")"
+                );
+
+        Entity app = createAndStartApplication(yaml);
+        waitForApplicationTasks(app);
+        DynamicFabric fabric = Iterables.getOnlyElement(Entities.descendants(app, DynamicFabric.class));
+        Iterable<TestEntity> members = Entities.descendants(fabric, TestEntity.class);
+        
+        assertEquals(Iterables.size(members), 2);
+    }
+
+    @Test
+    public void testCreateFabricWithLocationsInline() throws Exception {
+        String yaml = Joiner.on("\n").join(
+                "services:",
+                "- type: org.apache.brooklyn.entity.group.DynamicFabric",
+                "  memberSpec:",
+                "    $brooklyn:entitySpec:",
+                "      type: org.apache.brooklyn.core.test.entity.TestEntity",
+                "  locations:",
+                "  - byon(hosts=\"1.1.1.1\")",
+                "  - byon(hosts=\"1.1.1.2\")"
+                );
+
+        Entity app = createAndStartApplication(yaml);
+        waitForApplicationTasks(app);
+        DynamicFabric fabric = Iterables.getOnlyElement(Entities.descendants(app, DynamicFabric.class));
+        Iterable<TestEntity> members = Entities.descendants(fabric, TestEntity.class);
+        
+        assertEquals(Iterables.size(members), 2);
+    }
+
+    @Test
     public void testEntitySpecConfig() throws Exception {
         String yaml =
                 "services:\n"+
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
index 210f158..66d3cfe 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
@@ -24,7 +24,9 @@
 import java.io.StringReader;
 import java.util.Map;
 
-import com.google.common.collect.Iterables;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.mgmt.ExecutionContext;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
@@ -33,7 +35,6 @@
 import org.apache.brooklyn.core.config.external.AbstractExternalConfigSupplier;
 import org.apache.brooklyn.core.config.external.ExternalConfigSupplier;
 import org.apache.brooklyn.core.internal.BrooklynProperties;
-import org.apache.brooklyn.core.location.AbstractLocation;
 import org.apache.brooklyn.core.location.cloud.CloudLocationConfig;
 import org.apache.brooklyn.core.mgmt.internal.CampYamlParser;
 import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
@@ -48,10 +49,17 @@
 import org.testng.annotations.Test;
 
 import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
 
 @Test
 public class ExternalConfigYamlTest extends AbstractYamlTest {
     private static final Logger log = LoggerFactory.getLogger(ExternalConfigYamlTest.class);
+    
+    // Choose a small jar; it is downloaded in some tests.
+    // Pick an OSGi bundle that is not part of core brooklyn.
+    private static final String LIBRARY_URL = "https://repository.apache.org/content/groups/public/org/apache/logging/log4j/log4j-api/2.5/log4j-api-2.5.jar";
+    private static final String LIBRARY_SYMBOLIC_NAME = "org.apache.logging.log4j.api";
+    private static final String LIBRARY_VERSION = "2.5.0";
 
     @Override
     protected LocalManagementContext newTestManagementContext() {
@@ -60,8 +68,19 @@
         props.put("brooklyn.external.myprovider.mykey", "myval");
         props.put("brooklyn.external.myproviderWithoutMapArg", MyExternalConfigSupplierWithoutMapArg.class.getName());
 
+        props.put("brooklyn.external.myprovider.myCatalogId", "myId");
+        props.put("brooklyn.external.myprovider.myCatalogItemType", "template");
+        props.put("brooklyn.external.myprovider.myCatalogVersion", "1.2");
+        props.put("brooklyn.external.myprovider.myCatalogDescription", "myDescription");
+        props.put("brooklyn.external.myprovider.myCatalogDisplayName", "myDisplayName");
+        props.put("brooklyn.external.myprovider.myCatalogIconUrl", "classpath:///myIconUrl.png");
+        props.put("brooklyn.external.myprovider.myCatalogLibraryUrl", LIBRARY_URL);
+        props.put("brooklyn.external.myprovider.myCatalogLibraryName", LIBRARY_SYMBOLIC_NAME);
+        props.put("brooklyn.external.myprovider.myCatalogLibraryVersion", LIBRARY_VERSION);
+
         return LocalManagementContextForTests.builder(true)
                 .useProperties(props)
+                .disableOsgi(false)
                 .build();
     }
 
@@ -108,6 +127,97 @@
         assertEquals(Iterables.getOnlyElement( app.getLocations() ).config().get(MY_CONFIG_KEY), "myval");
     }
 
+    // Will download the given catalog library jar
+    @Test(groups="Integration")
+    public void testExternalisedCatalogConfigReferencedFromYaml() throws Exception {
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "    id: $brooklyn:external(\"myprovider\", \"myCatalogId\")",
+                "    itemType: $brooklyn:external(\"myprovider\", \"myCatalogItemType\")",
+                "    version: $brooklyn:external(\"myprovider\", \"myCatalogVersion\")",
+                "    description: $brooklyn:external(\"myprovider\", \"myCatalogDescription\")",
+                "    displayName: $brooklyn:external(\"myprovider\", \"myCatalogDisplayName\")",
+                "    iconUrl: $brooklyn:external(\"myprovider\", \"myCatalogIconUrl\")",
+                "    brooklyn.libraries:",
+                "    - $brooklyn:external(\"myprovider\", \"myCatalogLibraryUrl\")",
+                "",
+                "    item:",
+                "      services:",
+                "      - type: brooklyn.entity.database.mysql.MySqlNode");
+
+        catalog.addItems(yaml);
+
+        CatalogItem<Object, Object> item = Iterables.getOnlyElement(catalog.getCatalogItems());
+        CatalogBundle bundle = Iterables.getOnlyElement(item.getLibraries());
+        assertEquals(item.getId(), "myId:1.2");
+        assertEquals(item.getCatalogItemType(), CatalogItemType.TEMPLATE);
+        assertEquals(item.getVersion(), "1.2");
+        assertEquals(item.getDescription(), "myDescription");
+        assertEquals(item.getDisplayName(), "myDisplayName");
+        assertEquals(item.getIconUrl(), "classpath:///myIconUrl.png");
+        assertEquals(bundle.getUrl(), LIBRARY_URL);
+    }
+
+    // Will download the given catalog library jar
+    @Test(groups="Integration")
+    public void testExternalisedCatalogConfigReferencedFromYamlWithLibraryMap() throws Exception {
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "    id: myid",
+                "    itemType: template",
+                "    version: 1.2",
+                "    description: myDescription",
+                "    displayName: myDisplayName",
+                "    iconUrl: classpath:///myIconUrl.png",
+                "    brooklyn.libraries:",
+                "    - name: $brooklyn:external(\"myprovider\", \"myCatalogLibraryName\")",
+                "      version: $brooklyn:external(\"myprovider\", \"myCatalogLibraryVersion\")",
+                "      url: $brooklyn:external(\"myprovider\", \"myCatalogLibraryUrl\")",
+                "",
+                "    item:",
+                "      services:",
+                "      - type: brooklyn.entity.database.mysql.MySqlNode");
+
+        catalog.addItems(yaml);
+
+        CatalogItem<Object, Object> item = Iterables.getOnlyElement(catalog.getCatalogItems());
+        CatalogBundle bundle = Iterables.getOnlyElement(item.getLibraries());
+        assertEquals(bundle.getUrl(), LIBRARY_URL);
+        assertEquals(bundle.getSymbolicName(), LIBRARY_SYMBOLIC_NAME);
+        assertEquals(bundle.getVersion(), LIBRARY_VERSION);
+    }
+
+    // Will download the given catalog library jar
+    // Confirms "normal" behaviour, when all values in the catalog are hard-coded rather than using external config.
+    @Test(groups="Integration")
+    public void testNonExternalisedCatalogConfigReferencedFromYaml() throws Exception {
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: osgi.test",
+                "  itemType: template",
+                "  version: 1.3",
+                "  description: CentOS 6.6 With GUI - 1.3",
+                "  displayName: CentOS 6.6",
+                "  iconUrl: classpath:///centos.png",
+                "  brooklyn.libraries:",
+                "  - " + LIBRARY_URL,
+                "",
+                "  item:",
+                "    services:",
+                "    - type: brooklyn.entity.database.mysql.MySqlNode");
+
+        catalog.addItems(yaml);
+
+        CatalogItem<Object, Object> item = Iterables.getOnlyElement(catalog.getCatalogItems());
+        assertEquals(item.getId(), "osgi.test:1.3");
+        assertEquals(item.getCatalogItemType(), CatalogItemType.TEMPLATE);
+        assertEquals(item.getVersion(), "1.3");
+        assertEquals(item.getDescription(), "CentOS 6.6 With GUI - 1.3");
+        assertEquals(item.getDisplayName(), "CentOS 6.6");
+        assertEquals(item.getIconUrl(), "classpath:///centos.png");
+        assertEquals(Iterables.getOnlyElement(item.getLibraries()).getUrl(), LIBRARY_URL);
+    }
+
     @Test(groups="Integration")
     public void testExternalisedLocationConfigSetViaProvisioningPropertiesReferencedFromYaml() throws Exception {
         String yaml = Joiner.on("\n").join(
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/LocationsYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/LocationsYamlTest.java
index 269ff16..371a477 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/LocationsYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/LocationsYamlTest.java
@@ -28,15 +28,16 @@
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.MachineLocation;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.Test;
+import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation;
 import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.multi.MultiLocation;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
 import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
@@ -49,7 +50,7 @@
         String yaml = 
                 "location: localhost\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
         LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(app.getLocations());
@@ -61,7 +62,7 @@
         String yaml = 
                 "location: localhost:(name=myname)\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
         LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(app.getLocations());
@@ -74,7 +75,7 @@
                 "location:\n"+
                 "  localhost\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
         LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(app.getLocations());
@@ -88,7 +89,7 @@
                 "- localhost:(name=loc1)\n"+
                 "- localhost:(name=loc2)\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
         List<Location> locs = ImmutableList.copyOf(app.getLocations());
@@ -107,7 +108,7 @@
                 "    displayName: myname\n"+
                 "    myconfkey: myconfval\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
         LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(app.getLocations());
@@ -126,7 +127,7 @@
                 "    displayName: myname2\n"+
                 "    myconfkey: myconfval2\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
         List<Location> locs = ImmutableList.copyOf(app.getLocations());
@@ -145,7 +146,7 @@
         String yaml = 
                 "location: \n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
         assertTrue(app.getLocations().isEmpty(), "locs="+app.getLocations());
@@ -158,7 +159,7 @@
                 "locations:\n"+
                 "- localhost\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         try {
             createStartWaitAndLogApplication(new StringReader(yaml));
@@ -174,7 +175,7 @@
                 "location:\n"+
                 "- localhost\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         try {
             createStartWaitAndLogApplication(new StringReader(yaml));
@@ -189,11 +190,11 @@
                 "locations:\n"+
                 "- localhost:(name=loc1)\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
         Entity child = Iterables.getOnlyElement(app.getChildren());
-        LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(child.getLocations());
+        LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(Entities.getAllInheritedLocations(child));
         assertEquals(loc.getDisplayName(), "loc1");
     }
 
@@ -208,11 +209,11 @@
                 "    - 127.0.0.1\n"+
                 "    - brooklyn@127.0.0.2\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
         Entity child = Iterables.getOnlyElement(app.getChildren());
-        FixedListMachineProvisioningLocation<?> loc = (FixedListMachineProvisioningLocation<?>) Iterables.getOnlyElement(child.getLocations());
+        FixedListMachineProvisioningLocation<?> loc = (FixedListMachineProvisioningLocation<?>) Iterables.getOnlyElement(Entities.getAllInheritedLocations(child));
         Assert.assertEquals(loc.getChildren().size(), 2);
         
         SshMachineLocation l1 = (SshMachineLocation)loc.obtain();
@@ -229,11 +230,11 @@
                 "    user: root\n"+
                 "    hosts: \"{127.0.{0,127}.{1-2},brooklyn@127.0.0.127}\"\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
         Entity child = Iterables.getOnlyElement(app.getChildren());
-        FixedListMachineProvisioningLocation<?> loc = (FixedListMachineProvisioningLocation<?>) Iterables.getOnlyElement(child.getLocations());
+        FixedListMachineProvisioningLocation<?> loc = (FixedListMachineProvisioningLocation<?>) Iterables.getOnlyElement(Entities.getAllInheritedLocations(child));
         Assert.assertEquals(loc.getChildren().size(), 5);
         
         assertUserAddress((SshMachineLocation)loc.obtain(), "root", "127.0.0.1");
@@ -257,11 +258,11 @@
                 "      hosts:\n"+
                 "      - 127.0.0.127\n"+
                 "services:\n"+
-                "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+                "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
         
         Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
         Entity child = Iterables.getOnlyElement(app.getChildren());
-        MultiLocation<?> loc = (MultiLocation<?>) Iterables.getOnlyElement(child.getLocations());
+        MultiLocation<?> loc = (MultiLocation<?>) Iterables.getOnlyElement(Entities.getAllInheritedLocations(child));
         Assert.assertEquals(loc.getSubLocations().size(), 2);
         
         assertUserAddress((SshMachineLocation)loc.obtain(), "root", "127.0.0.1");
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
index 47925e5..f792d65 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
@@ -32,6 +32,7 @@
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
 import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest;
 import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
 import org.apache.brooklyn.core.typereg.RegisteredTypes;
@@ -187,7 +188,7 @@
             "  - type: org.apache.brooklyn.entity.stock.BasicStartable");
 
         Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
-        Location location = Iterables.getOnlyElement(simpleEntity.getLocations());
+        Location location = Iterables.getOnlyElement(Entities.getAllInheritedLocations(simpleEntity));
         assertEquals(location.getClass().getName(), locType);
         assertEquals(location.getConfig(new BasicConfigKey<String>(String.class, "config1")), "config1");
         assertEquals(location.getConfig(new BasicConfigKey<String>(String.class, "config2")), "config2 override");
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
index c3dbc48..d83711c 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
@@ -18,18 +18,37 @@
  */
 package org.apache.brooklyn.camp.brooklyn.catalog;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.mgmt.BrooklynTags;
+import org.apache.brooklyn.core.mgmt.BrooklynTags.NamedStringTag;
+import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
 import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest;
+import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
 import org.apache.brooklyn.core.typereg.RegisteredTypes;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.stock.BasicApplication;
+import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.test.support.TestResourceUnavailableException;
+import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.osgi.OsgiTestResources;
 import org.testng.Assert;
 import org.testng.TestListenerAdapter;
 import org.testng.TestNG;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.Iterables;
+
 
 public class CatalogYamlTemplateTest extends AbstractYamlTest {
     
@@ -63,6 +82,174 @@
         deleteCatalogEntity("t1");
     }
 
+    public void testServiceTypeEntityOfTypeCatalogTemplateNotWrapped() throws Exception {
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  id: t1",
+                "  item_type: template",
+                "  name: myT1",
+                "  item:",
+                "    services:",
+                "    - type: " + TestEntity.class.getName());
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  id: t2",
+                "  item_type: template",
+                "  name: myT2",
+                "  item:",
+                "    services:",
+                "    - type: t1",
+                "    - type: t1");
+
+        Entity app = createAndStartApplication(
+                "services:",
+                "- type: t2");
+        waitForApplicationTasks(app);
+        
+        Entities.dumpInfo(app);
+        Entity t1a = Iterables.get(app.getChildren(), 0);
+        Entity t1b = Iterables.get(app.getChildren(), 1);
+        assertEquals(app.getChildren().size(), 2);
+        assertEquals(t1a.getChildren().size(), 0);
+        assertEquals(t1b.getChildren().size(), 0);
+        
+        assertTrue(app instanceof BasicApplication);
+        assertTrue(t1a instanceof TestEntity);
+        assertTrue(t1b instanceof TestEntity);
+    }
+
+    @Test
+    public void testChildEntityOfTypeCatalogTemplateNotWrapped() throws Exception {
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  id: t1",
+                "  item_type: template",
+                "  name: myT1",
+                "  item:",
+                "    services:",
+                "    - type: " + TestEntity.class.getName());
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  id: t2",
+                "  item_type: template",
+                "  name: myT2",
+                "  item:",
+                "    services:",
+                "    - type: " + TestEntity.class.getName(),
+                "      brooklyn.children:",
+                "      - type: t1");
+
+        Entity app = createAndStartApplication(
+                "services:",
+                "- type: t2");
+        waitForApplicationTasks(app);
+        
+        Entities.dumpInfo(app);
+        Entity t2 = Iterables.getOnlyElement(app.getChildren());
+        Entity t1 = Iterables.getOnlyElement(t2.getChildren());
+        assertEquals(t1.getChildren().size(), 0);
+        
+        assertTrue(app instanceof BasicApplication);
+        assertTrue(t1 instanceof TestEntity);
+        assertTrue(t2 instanceof TestEntity);
+    }
+
+    @Test
+    public void testMemberSpecEntityOfTypeCatalogTemplateNotWrapped() throws Exception {
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  id: t1",
+                "  item_type: template",
+                "  name: myT1",
+                "  item:",
+                "    services:",
+                "    - type: " + TestEntity.class.getName());
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  id: t2",
+                "  item_type: template",
+                "  name: myT2",
+                "  item:",
+                "    services:",
+                "    - type: " + DynamicCluster.class.getName(),
+                "      brooklyn.config:",
+                "        memberSpec:",
+                "          $brooklyn:entitySpec:",
+                "            type: t1",
+                "        cluster.initial.size: 1");
+
+        Entity app = createAndStartApplication(
+                "location: localhost",
+                "services:",
+                "- type: t2");
+        waitForApplicationTasks(app);
+        
+        Entities.dumpInfo(app);
+        DynamicCluster t2 = (DynamicCluster) Iterables.getOnlyElement(app.getChildren());
+        Entity t1 = Iterables.getOnlyElement(t2.getMembers());
+        assertEquals(t1.getChildren().size(), 0);
+        
+        assertTrue(app instanceof BasicApplication);
+        assertTrue(t2 instanceof DynamicCluster);
+        assertTrue(t1 instanceof TestEntity);
+    }
+
+    @Test
+    public void testMetadataOnSpecCreatedFromItem() throws Exception {
+        makeItem();
+        EntitySpec<? extends Application> spec = EntityManagementUtils.createEntitySpecForApplication(mgmt(), 
+            "services: [ { type: t1 } ]\n" +
+            "location: localhost");
+        
+        List<NamedStringTag> yamls = BrooklynTags.findAll(BrooklynTags.YAML_SPEC_KIND, spec.getTags());
+        Assert.assertEquals(yamls.size(), 1, "Expected 1 yaml tag; instead had: "+yamls);
+        String yaml = Iterables.getOnlyElement(yamls).getContents();
+        Asserts.assertStringContains(yaml, "services:", "t1", "localhost");
+        
+        EntitySpec<?> child = Iterables.getOnlyElement( spec.getChildren() );
+        Assert.assertEquals(child.getType().getName(), SIMPLE_ENTITY_TYPE);
+        Assert.assertEquals(child.getCatalogItemId(), "t1:"+TEST_VERSION);
+    }
+    
+    @Test
+    public void testMetadataOnSpecCreatedFromItemReferencingAnApp() throws Exception {
+        // this nested ref to an app caused nested plan contents also to be recorded,
+        // due to how tags are merged. the *first* one is the most important, however,
+        // and ordering of tags should guarantee that.
+        // similarly ensure we get the right outermost non-null catalog item id.
+        addCatalogItems(
+              "brooklyn.catalog:",
+              "  version: '1'",
+              "  items:",
+              "  - id: app1",
+              "    name: myApp1",
+              "    item:",
+              "      type: org.apache.brooklyn.entity.stock.BasicApplication",
+              "      brooklyn.config: { foo: bar }",
+              "  - id: app1r",
+              "    item_type: template",
+              "    item:",
+              "      services:",
+              "      - type: app1",
+              "        brooklyn.config:",
+              "          foo: boo"
+            );
+        
+        EntitySpec<? extends Application> spec = EntityManagementUtils.createEntitySpecForApplication(mgmt(),
+            "services: [ { type: app1r } ]\n" +
+            "location: localhost");
+        
+        List<NamedStringTag> yamls = BrooklynTags.findAll(BrooklynTags.YAML_SPEC_KIND, spec.getTags());
+        Assert.assertTrue(yamls.size() >= 1, "Expected at least 1 yaml tag; instead had: "+yamls);
+        String yaml = yamls.iterator().next().getContents();
+        Asserts.assertStringContains(yaml, "services:", "type: app1r", "localhost");
+        
+        Assert.assertEquals(spec.getChildren().size(), 0);
+        Assert.assertEquals(spec.getType(), BasicApplication.class);
+        Assert.assertEquals(ConfigBag.newInstance(spec.getConfig()).getStringKey("foo"), "boo");
+        Assert.assertEquals(spec.getCatalogItemId(), "app1r:1");
+    }
+    
     private RegisteredType makeItem() {
         TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
         
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/resources/test-entity-basic-template.yaml b/brooklyn-server/camp/camp-brooklyn/src/test/resources/test-entity-basic-template.yaml
index b690309..7ace993 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/resources/test-entity-basic-template.yaml
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/resources/test-entity-basic-template.yaml
@@ -19,6 +19,6 @@
 description: TestEntity with templated brooklyn.config and additional config (such as services)
 origin: https://github.com/apache/incubator-brooklyn
 services:
-- serviceType: org.apache.brooklyn.core.test.entity.TestEntity
+- type: org.apache.brooklyn.core.test.entity.TestEntity
   name: testentity
 # should have nothing below here as the test appends things underneath
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
index 96cf452..41a0a9c 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -41,12 +41,14 @@
 import org.apache.brooklyn.core.catalog.CatalogPredicates;
 import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
 import org.apache.brooklyn.core.location.BasicLocationRegistry;
+import org.apache.brooklyn.core.mgmt.internal.CampYamlParser;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.typereg.BrooklynTypePlanTransformer;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.javalang.AggregateClassLoader;
@@ -395,14 +397,39 @@
 
         if (sourceYaml==null) sourceYaml = new Yaml().dump(itemMetadata);
 
-        Map<Object,Object> catalogMetadata = MutableMap.builder().putAll(parentMetadata).putAll(itemMetadata).build();
+        Map<?, ?> itemMetadataWithoutItemDef = MutableMap.builder()
+                .putAll(itemMetadata)
+                .remove("item")
+                .remove("items")
+                .build();
         
+        // Parse CAMP-YAML DSL in item metadata (but not in item or items - those will be parsed only when used). 
+        CampYamlParser parser = mgmt.getConfig().getConfig(CampYamlParser.YAML_PARSER_KEY);
+        if (parser != null) {
+            itemMetadataWithoutItemDef = parser.parse((Map<String, Object>) itemMetadataWithoutItemDef);
+            try {
+                itemMetadataWithoutItemDef = (Map<String, Object>) Tasks.resolveDeepValue(itemMetadataWithoutItemDef, Object.class, mgmt.getServerExecutionContext());
+            } catch (Exception e) {
+                throw Exceptions.propagate(e);
+            }
+            
+        } else {
+            log.info("No Camp-YAML parser regsitered for parsing catalog item DSL; skipping DSL-parsing");
+        }
+
+        Map<Object,Object> catalogMetadata = MutableMap.<Object, Object>builder()
+                .putAll(parentMetadata)
+                .putAll(itemMetadataWithoutItemDef)
+                .putIfNotNull("item", itemMetadata.get("item"))
+                .putIfNotNull("items", itemMetadata.get("items"))
+                .build();
+
         // brooklyn.libraries we treat specially, to append the list, with the child's list preferred in classloading order
         // `libraries` is supported in some places as a legacy syntax; it should always be `brooklyn.libraries` for new apps
         // TODO in 0.8.0 require brooklyn.libraries, don't allow "libraries" on its own
-        List<?> librariesNew = MutableList.copyOf(getFirstAs(itemMetadata, List.class, "brooklyn.libraries", "libraries").orNull());
+        List<?> librariesNew = MutableList.copyOf(getFirstAs(itemMetadataWithoutItemDef, List.class, "brooklyn.libraries", "libraries").orNull());
         Collection<CatalogBundle> libraryBundlesNew = CatalogItemDtoAbstract.parseLibraries(librariesNew);
-        
+
         List<?> librariesCombined = MutableList.copyOf(librariesNew)
             .appendAll(getFirstAs(parentMetadata, List.class, "brooklyn.libraries", "libraries").orNull());
         if (!librariesCombined.isEmpty())
@@ -413,7 +440,7 @@
         // (this load is required for the scan below and I think also for yaml resolution)
         CatalogUtils.installLibraries(mgmt, libraryBundlesNew);
 
-        Boolean scanJavaAnnotations = getFirstAs(itemMetadata, Boolean.class, "scanJavaAnnotations", "scan_java_annotations").orNull();
+        Boolean scanJavaAnnotations = getFirstAs(itemMetadataWithoutItemDef, Boolean.class, "scanJavaAnnotations", "scan_java_annotations").orNull();
         if (scanJavaAnnotations==null || !scanJavaAnnotations) {
             // don't scan
         } else {
@@ -433,8 +460,8 @@
         if (items!=null) {
             int count = 0;
             for (Map<?,?> i: ((List<Map<?,?>>)items)) {
-                collectCatalogItems(Yamls.getTextOfYamlAtPath(sourceYaml, "items", count).getMatchedYamlTextOrWarn(), 
-                    i, result, catalogMetadata);
+                collectCatalogItems(Yamls.getTextOfYamlAtPath(sourceYaml, "items", count).getMatchedYamlTextOrWarn(),
+                        i, result, catalogMetadata);
                 count++;
             }
         }
@@ -463,8 +490,10 @@
         PlanInterpreterGuessingType planInterpreter = new PlanInterpreterGuessingType(null, item, sourceYaml, itemType, libraryBundles, result).reconstruct();
         if (!planInterpreter.isResolved()) {
             throw Exceptions.create("Could not resolve item"
-                + (Strings.isNonBlank(id) ? " "+id : Strings.isNonBlank(symbolicName) ? " "+symbolicName : Strings.isNonBlank(name) ? name : "")
-                // better not to show yaml, takes up lots of space, and with multiple plan transformers there might be multiple errors 
+                + (Strings.isNonBlank(id) ? " '"+id+"'" : Strings.isNonBlank(symbolicName) ? " '"+symbolicName+"'" : Strings.isNonBlank(name) ? " '"+name+"'" : "")
+                // better not to show yaml, takes up lots of space, and with multiple plan transformers there might be multiple errors; 
+                // some of the errors themselves may reproduce it
+                // (ideally in future we'll be able to return typed errors with caret position of error)
 //                + ":\n"+sourceYaml
                 , planInterpreter.getErrors());
         }
@@ -582,12 +611,12 @@
         return oldValue;
     }
 
-    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromLocal(ManagementContext mgmt, Map<Object, Object> catalogMetadata) {
+    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromLocal(ManagementContext mgmt, Map<?, ?> catalogMetadata) {
         CatalogDto dto = CatalogDto.newNamedInstance("Local Scanned Catalog", "All annotated Brooklyn entities detected in the classpath", "scanning-local-classpath");
         return scanAnnotationsInternal(mgmt, new CatalogDo(dto), catalogMetadata);
     }
     
-    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromBundles(ManagementContext mgmt, Collection<CatalogBundle> libraries, Map<Object, Object> catalogMetadata) {
+    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromBundles(ManagementContext mgmt, Collection<CatalogBundle> libraries, Map<?, ?> catalogMetadata) {
         CatalogDto dto = CatalogDto.newNamedInstance("Bundles Scanned Catalog", "All annotated Brooklyn entities detected in bundles", "scanning-bundles-classpath-"+libraries.hashCode());
         List<String> urls = MutableList.of();
         for (CatalogBundle b: libraries) {
@@ -608,7 +637,7 @@
         return scanAnnotationsInternal(mgmt, subCatalog, catalogMetadata);
     }
     
-    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsInternal(ManagementContext mgmt, CatalogDo subCatalog, Map<Object, Object> catalogMetadata) {
+    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsInternal(ManagementContext mgmt, CatalogDo subCatalog, Map<?, ?> catalogMetadata) {
         // TODO this does java-scanning only;
         // the call when scanning bundles should use the CatalogItem instead and use OSGi when loading for scanning
         // (or another scanning mechanism).  see comments on CatalogClasspathDo.load
@@ -989,7 +1018,7 @@
         };
     }
     
-    private static <T,SpecT> Function<CatalogItemDo<T, SpecT>, CatalogItem<T,SpecT>> itemDoToDtoAddingSelectedMetadataDuringScan(final Map<Object, Object> catalogMetadata) {
+    private static <T,SpecT> Function<CatalogItemDo<T, SpecT>, CatalogItem<T,SpecT>> itemDoToDtoAddingSelectedMetadataDuringScan(final Map<?, ?> catalogMetadata) {
         return new Function<CatalogItemDo<T,SpecT>, CatalogItem<T,SpecT>>() {
             @Override
             public CatalogItem<T,SpecT> apply(@Nullable CatalogItemDo<T,SpecT> item) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java
index e000997..3fd4b05 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java
@@ -38,6 +38,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ImmutableSet;
+
 /**
  * Users can extend this to define the entities in their application, and the relationships between
  * those entities. Users should override the {@link #init()} method, and in there should create 
@@ -143,7 +145,8 @@
     @Override
     public void start(Collection<? extends Location> locations) {
         this.addLocations(locations);
-        Collection<? extends Location> locationsToUse = getLocations();
+        // 2016-01: only pass locations passed to us, as per ML discussion
+        Collection<? extends Location> locationsToUse = locations==null ? ImmutableSet.<Location>of() : locations;
         ServiceProblemsLogic.clearProblemsIndicator(this, START);
         ServiceStateLogic.ServiceNotUpLogic.updateNotUpIndicator(this, Attributes.SERVICE_STATE_ACTUAL, "Application starting");
         setExpectedStateAndRecordLifecycleEvent(Lifecycle.STARTING);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index 0599373..5422fb6 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -874,15 +874,18 @@
 
     @Override
     public void addLocations(Collection<? extends Location> newLocations) {
+        if (newLocations==null || newLocations.isEmpty()) {
+            return;
+        }
         synchronized (locations) {
             List<Location> oldLocations = locations.get();
-            Set<Location> truelyNewLocations = Sets.newLinkedHashSet(newLocations);
-            truelyNewLocations.removeAll(oldLocations);
-            if (truelyNewLocations.size() > 0) {
-                locations.set(ImmutableList.<Location>builder().addAll(oldLocations).addAll(truelyNewLocations).build());
+            Set<Location> trulyNewLocations = Sets.newLinkedHashSet(newLocations);
+            trulyNewLocations.removeAll(oldLocations);
+            if (trulyNewLocations.size() > 0) {
+                locations.set(ImmutableList.<Location>builder().addAll(oldLocations).addAll(trulyNewLocations).build());
             }
             
-            for (Location loc : truelyNewLocations) {
+            for (Location loc : trulyNewLocations) {
                 sensors().emit(AbstractEntity.LOCATION_ADDED, loc);
             }
         }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
index 59e3ec1..9c8ebc8 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
@@ -80,6 +80,7 @@
 import org.apache.brooklyn.core.objs.proxy.EntityProxyImpl;
 import org.apache.brooklyn.core.sensor.DependentConfiguration;
 import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.ResourceUtils;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
@@ -804,10 +805,15 @@
                 ((ManagementContextInternal)mgmt).terminate();
             }
             if (error.get() != null) throw Exceptions.propagate(error.get());
-        } catch (InterruptedException e) {
-            throw Exceptions.propagate(e);
-        } catch (ExecutionException e) {
-            throw Exceptions.propagate(e);
+        } catch (Exception e) {
+            if (!mgmt.isRunning()) {
+                // we've checked this above so it would only happen if a different thread stopped it;
+                // this does happen sometimes e.g. in CliTest where the server shutdown occurs concurrently
+                log.debug("Destroying apps gave an error, but mgmt context was concurrently stopped so not really a problem; swallowing (unless fatal): "+e);
+                Exceptions.propagateIfFatal(e);
+            } else {
+                throw Exceptions.propagate(e);
+            }
         } finally {
             executor.shutdownNow();
         }
@@ -1183,4 +1189,13 @@
         return root;
     }
 
+    public static Set<Location> getAllInheritedLocations(Entity entity) {
+        Set<Location> result = MutableSet.of();
+        while (entity!=null) {
+            result.addAll(entity.getLocations());
+            entity = entity.getParent();
+        }
+        return result;
+    }
+    
 }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
index 4056eca..3602bee 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
@@ -21,6 +21,8 @@
 import java.util.Collection;
 import java.util.Map;
 
+import javax.annotation.Nullable;
+
 import org.apache.brooklyn.api.effector.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntityLocal;
@@ -48,7 +50,7 @@
 @Beta
 public interface EntityInternal extends BrooklynObjectInternal, EntityLocal, Rebindable {
     
-    void addLocations(Collection<? extends Location> locations);
+    void addLocations(@Nullable Collection<? extends Location> locations);
 
     void removeLocations(Collection<? extends Location> locations);
 
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java
index 68c9398..36e6ba8 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java
@@ -31,6 +31,21 @@
  */
 public interface Resizable {
 
+    /**
+     * Indicates that resizing up to the desired size is not possible - only resized to the 
+     * {@link Resizable#getCurrentSize()}, because there is insufficient capacity.
+     */
+    public static class InsufficientCapacityException extends RuntimeException {
+        private static final long serialVersionUID = 953230498564942446L;
+        
+        public InsufficientCapacityException(String msg) {
+            super(msg);
+        }
+        public InsufficientCapacityException(String msg, Throwable cause) {
+            super(msg, cause);
+        }
+    }
+    
     MethodEffector<Integer> RESIZE = new MethodEffector<Integer>(Resizable.class, "resize");
 
     /**
@@ -38,6 +53,9 @@
      *
      * @param desiredSize the new size of the entity group.
      * @return the new size of the group.
+     * 
+     * @throws InsufficientCapacityException If the request was to grow, but there is no capacity to grow to
+     *         the desired size.
      */
     @Effector(description="Changes the size of the entity (e.g. the number of nodes in a cluster)")
     Integer resize(@EffectorParam(name="desiredSize", description="The new size of the cluster") Integer desiredSize);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
index 4954393..60fb061 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
@@ -430,7 +430,9 @@
     public List<Location> resolve(Iterable<?> spec) {
         List<Location> result = new ArrayList<Location>();
         for (Object id : spec) {
-            if (id instanceof String) {
+            if (id==null) {
+                // drop a null entry
+            } if (id instanceof String) {
                 result.add(resolve((String) id));
             } else if (id instanceof Location) {
                 result.add((Location) id);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
index a08eb56..0ddc79a 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
@@ -22,6 +22,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.brooklyn.util.collections.MutableList;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonProperty;
 
@@ -74,7 +75,7 @@
         public boolean equals(Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
-            ListTag that = (ListTag) o;
+            ListTag<?> that = (ListTag<?>) o;
             return list == null ? that.list == null : list.equals(that.list);
         }
 
@@ -124,5 +125,14 @@
         }
         return null;
     }
-    
+
+    public static List<NamedStringTag> findAll(String kind, Iterable<Object> tags) {
+        List<NamedStringTag> result = MutableList.of();
+        for (Object object: tags) {
+            if (object instanceof NamedStringTag && kind.equals(((NamedStringTag)object).kind))
+                result.add( (NamedStringTag) object );
+        }
+        return result;
+    }
+
 }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
index ef461db..2f37e7c 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
@@ -50,7 +50,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.annotations.Beta;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -67,11 +66,12 @@
     /**
      * A marker config value which indicates that an {@link Application} entity was created automatically,
      * needed because a plan might give multiple top-level entities or a non-Application top-level entity,
-     * but brooklyn requires an {@link Application} at the root.
+     * in a context where Brooklyn requires an {@link Application} at the root.
      * <p>
      * Typically when such a wrapper app wraps another {@link Application}
-     * (or when we are adding to an existing entity and it wraps multiple {@link Entity} instances)
-     * it will be unwrapped. See {@link #newWrapperApp()} and {@link #unwrapApplication(EntitySpec)}.
+     * (or where we are looking for a single {@link Entity}, or a list to add, and they are so wrapped)
+     * it will be unwrapped. 
+     * See {@link #newWrapperApp()} and {@link #unwrapApplication(EntitySpec)}.
      */
     public static final ConfigKey<Boolean> WRAPPER_APP_MARKER = ConfigKeys.newBooleanConfigKey("brooklyn.wrapper_app");
 
@@ -159,21 +159,16 @@
 
         // see whether we can promote children
         List<EntitySpec<?>> specs = MutableList.of();
-        if (canPromoteChildrenInWrappedApplication(specA)) {
-            // we can promote
-            for (EntitySpec<?> specC: specA.getChildren()) {
-                mergeWrapperParentSpecToChildEntity(specA, specC);
-                specs.add(specC);
-            }
-        } else {
+        if (!canUnwrapEntity(specA)) {
             // if not promoting, set a nice name if needed
             if (Strings.isEmpty(specA.getDisplayName())) {
                 int size = specA.getChildren().size();
                 String childrenCountString = size+" "+(size!=1 ? "children" : "child");
                 specA.displayName("Dynamically added "+childrenCountString);
             }
-            specs.add(specA);
         }
+        
+        specs.add(unwrapEntity(specA));
 
         final List<Entity> children = MutableList.of();
         for (EntitySpec<?> spec: specs) {
@@ -219,75 +214,111 @@
 
         return CreationResult.of(children, task);
     }
-
-    /** if an application should be unwrapped, it does so, returning the child; otherwise returns the argument passed in.
-     * use {@link #canPromoteWrappedApplication(EntitySpec)} to test whether it will unwrap. */
-    public static EntitySpec<? extends Application> unwrapApplication(EntitySpec<? extends Application> wrapperApplication) {
-        if (canPromoteWrappedApplication(wrapperApplication)) {
-            @SuppressWarnings("unchecked")
-            EntitySpec<? extends Application> wrappedApplication = (EntitySpec<? extends Application>) Iterables.getOnlyElement( wrapperApplication.getChildren() );
-
-            // if promoted, apply the transformations done to the app
-            // (transformations will be done by the resolveSpec call above, but we are collapsing oldApp so transfer to app=newApp)
-            EntityManagementUtils.mergeWrapperParentSpecToChildEntity(wrapperApplication, wrappedApplication);
-            return wrappedApplication;
+    
+    /** Unwraps a single {@link Entity} if appropriate. See {@link #WRAPPER_APP_MARKER}.
+     * Also see {@link #canUnwrapEntity(EntitySpec)} to test whether it will unwrap. */
+    public static EntitySpec<? extends Entity> unwrapEntity(EntitySpec<? extends Entity> wrapperApplication) {
+        if (!canUnwrapEntity(wrapperApplication)) {
+            return wrapperApplication;
         }
-        return wrapperApplication;
+        EntitySpec<?> wrappedEntity = Iterables.getOnlyElement(wrapperApplication.getChildren());
+        @SuppressWarnings("unchecked")
+        EntitySpec<? extends Application> wrapperApplicationTyped = (EntitySpec<? extends Application>) wrapperApplication;
+        EntityManagementUtils.mergeWrapperParentSpecToChildEntity(wrapperApplicationTyped, wrappedEntity);
+        return wrappedEntity;
+    }
+    
+    /** Unwraps a wrapped {@link Application} if appropriate.
+     * This is like {@link #canUnwrapEntity(EntitySpec)} with an additional check that the wrapped child is an {@link Application}. 
+     * See {@link #WRAPPER_APP_MARKER} for an overview. 
+     * Also see {@link #canUnwrapApplication(EntitySpec)} to test whether it will unwrap. */
+    public static EntitySpec<? extends Application> unwrapApplication(EntitySpec<? extends Application> wrapperApplication) {
+        if (!canUnwrapApplication(wrapperApplication)) {
+            return wrapperApplication;
+        }
+        @SuppressWarnings("unchecked")
+        EntitySpec<? extends Application> wrappedApplication = (EntitySpec<? extends Application>) unwrapEntity(wrapperApplication);
+        return wrappedApplication;
     }
     
     /** Modifies the child so it includes the inessential setup of its parent,
      * for use when unwrapping specific children, but a name or other item may have been set on the parent.
      * See {@link #WRAPPER_APP_MARKER}. */
-    @Beta //where should this live long-term?
-    public static void mergeWrapperParentSpecToChildEntity(EntitySpec<? extends Application> wrapperParent, EntitySpec<?> wrappedChild) {
-        if (Strings.isNonEmpty(wrapperParent.getDisplayName()))
+    private static void mergeWrapperParentSpecToChildEntity(EntitySpec<? extends Application> wrapperParent, EntitySpec<?> wrappedChild) {
+        if (Strings.isNonEmpty(wrapperParent.getDisplayName())) {
             wrappedChild.displayName(wrapperParent.getDisplayName());
-        if (!wrapperParent.getLocations().isEmpty())
-            wrappedChild.locations(wrapperParent.getLocations());
-        if (!wrapperParent.getParameters().isEmpty()) {
-            wrappedChild.parameters(wrapperParent.getParameters());
         }
+        
+        wrappedChild.locations(wrapperParent.getLocations());
+        
+        if (!wrapperParent.getParameters().isEmpty())
+            wrappedChild.parametersReplace(wrapperParent.getParameters());
 
-        // NB: this clobbers child config; might prefer to deeply merge maps etc
-        // (but this should not be surprising, as unwrapping is often parameterising the nested blueprint, so outer config should dominate) 
+        // prefer the wrapper ID (change in 2016-01); see notes on the catalogItemIdIfNotNull method
+        wrappedChild.catalogItemIdIfNotNull(wrapperParent.getCatalogItemId());
+
+        // NB: this clobber's child config wherever they conflict; might prefer to deeply merge maps etc
+        // (or maybe even prevent the merge in these cases; 
+        // not sure there is a compelling reason to have config on a pure-wrapper parent)
         Map<ConfigKey<?>, Object> configWithoutWrapperMarker = Maps.filterKeys(wrapperParent.getConfig(), Predicates.not(Predicates.<ConfigKey<?>>equalTo(EntityManagementUtils.WRAPPER_APP_MARKER)));
         wrappedChild.configure(configWithoutWrapperMarker);
         wrappedChild.configure(wrapperParent.getFlags());
         
-        // TODO copying tags to all entities is not ideal;
-        // in particular the BrooklynTags.YAML_SPEC tag will show all entities if the root has multiple
-        wrappedChild.tags(wrapperParent.getTags());
+        // copying tags to all entities may be something the caller wants to control,
+        // e.g. if we're adding multiple, the caller might not want to copy the parent
+        // (the BrooklynTags.YAML_SPEC tag will include the parents source including siblings),
+        // but OTOH they might because otherwise the parent's tags might get lost.
+        // also if we are unwrapping multiple registry references we will get the YAML_SPEC for each;
+        // putting the parent's tags first however causes the preferred (outer) one to be retrieved first.
+        wrappedChild.tagsReplace(MutableList.copyOf(wrapperParent.getTags()).appendAll(wrappedChild.getTags()));
     }
 
     public static EntitySpec<? extends Application> newWrapperApp() {
         return EntitySpec.create(BasicApplication.class).configure(WRAPPER_APP_MARKER, true);
     }
     
-    /** returns true if the spec is for an empty-ish wrapper app contianing an application, 
-     * for use when adding from a plan specifying an application which was wrapped because it had to be.
+    /** As {@link #canUnwrapEntity(EntitySpec)}
+     * but additionally requiring that the wrapped item is an {@link Application},
+     * for use when the context requires an {@link Application} ie a root of a spec.
      * @see #WRAPPER_APP_MARKER */
+    public static boolean canUnwrapApplication(EntitySpec<? extends Application> wrapperApplication) {
+        if (!canUnwrapEntity(wrapperApplication)) return false;
+
+        EntitySpec<?> childSpec = Iterables.getOnlyElement(wrapperApplication.getChildren());
+        return (childSpec.getType()!=null && Application.class.isAssignableFrom(childSpec.getType()));
+    }
+    /** @deprecated since 0.9.0 use {@link #canUnwrapApplication(EntitySpec)} */ @Deprecated
     public static boolean canPromoteWrappedApplication(EntitySpec<? extends Application> app) {
-        if (!hasSingleChild(app))
-            return false;
-
-        EntitySpec<?> childSpec = Iterables.getOnlyElement(app.getChildren());
-        if (childSpec.getType()==null || !Application.class.isAssignableFrom(childSpec.getType()))
-            return false;
-
-        return canPromoteChildrenInWrappedApplication(app);
+        return canUnwrapApplication(app);
     }
     
-    /** returns true if the spec is for a wrapper app with no important settings, wrapping a single child. 
-     * for use when adding from a plan specifying multiple entities but nothing significant at the application level.
-     * @see #WRAPPER_APP_MARKER */
-    public static boolean canPromoteChildrenInWrappedApplication(EntitySpec<? extends Application> spec) {
+    /** Returns true if the spec is for a wrapper app with no important settings, wrapping a single child entity. 
+     * for use when adding from a plan specifying multiple entities but there is nothing significant at the application level,
+     * and the context would like to flatten it to remove the wrapper yielding just a single entity.
+     * (but note the result is not necessarily an {@link Application}; 
+     * see {@link #canUnwrapApplication(EntitySpec)} if that is required).
+     * <p>
+     * Note callers will normally use one of {@link #unwrapEntity(EntitySpec)} or {@link #unwrapApplication(EntitySpec)}.
+     * 
+     * @see #WRAPPER_APP_MARKER for an overview */
+    public static boolean canUnwrapEntity(EntitySpec<? extends Entity> spec) {
         return isWrapperApp(spec) && hasSingleChild(spec) &&
-                //equivalent to no keys starting with "brooklyn."
-                spec.getEnrichers().isEmpty() &&
-                spec.getEnricherSpecs().isEmpty() &&
-                spec.getInitializers().isEmpty() &&
-                spec.getPolicies().isEmpty() &&
-                spec.getPolicySpecs().isEmpty();
+            // these "brooklyn.*" items on the app rather than the child absolutely prevent unwrapping
+            // as their semantics could well be different whether they are on the parent or the child
+            spec.getEnrichers().isEmpty() &&
+            spec.getEnricherSpecs().isEmpty() &&
+            spec.getInitializers().isEmpty() &&
+            spec.getPolicies().isEmpty() &&
+            spec.getPolicySpecs().isEmpty() &&
+            // these items prevent merge only if they are defined at both levels
+            (spec.getLocations().isEmpty() || Iterables.getOnlyElement(spec.getChildren()).getLocations().isEmpty())
+            // TODO what should we do with parameters? currently clobbers due to EntitySpec.parameters(...) behaviour.
+//            && (spec.getParameters().isEmpty() || Iterables.getOnlyElement(spec.getChildren()).getParameters().isEmpty())
+            ;
+    }
+    /** @deprecated since 0.9.0 use {@link #canUnwrapEntity(EntitySpec)} */ @Deprecated
+    public static boolean canPromoteChildrenInWrappedApplication(EntitySpec<? extends Application> spec) {
+        return canUnwrapEntity(spec);
     }
 
     public static boolean isWrapperApp(EntitySpec<?> spec) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
index d41e059..da9fcae 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
@@ -372,10 +372,15 @@
         return configMap;
     }
 
+    private final Object locationRegistrySemaphore = new Object();
+    
     @Override
-    public synchronized LocationRegistry getLocationRegistry() {
-        if (locationRegistry==null) locationRegistry = new BasicLocationRegistry(this);
-        return locationRegistry;
+    public LocationRegistry getLocationRegistry() {
+        // NB: can deadlock if synched on whole LMC
+        synchronized (locationRegistrySemaphore) {
+            if (locationRegistry==null) locationRegistry = new BasicLocationRegistry(this);
+            return locationRegistry;
+        }
     }
 
     @Override
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/BrooklynShutdownHooks.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/BrooklynShutdownHooks.java
index 677a4f6..91ca5dc 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/BrooklynShutdownHooks.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/BrooklynShutdownHooks.java
@@ -187,7 +187,9 @@
             }
             entitiesToStop.addAll(entitiesToStopOnShutdown);
             for (ManagementContext mgmt: managementContextsToStopAppsOnShutdown) {
-                entitiesToStop.addAll(mgmt.getApplications());
+                if (mgmt.isRunning()) {
+                    entitiesToStop.addAll(mgmt.getApplications());
+                }
             }
             
             if (entitiesToStop.isEmpty()) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EffectorUtils.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EffectorUtils.java
index c6ec3ac..f8bb7cb 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EffectorUtils.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EffectorUtils.java
@@ -149,6 +149,9 @@
         }
         if (newArgsNeeded>0)
             throw new IllegalArgumentException("Invalid arguments (missing "+newArgsNeeded+") for effector "+eff+": "+m);
+        if (!m.isEmpty()) {
+            log.warn("Unsupported parameter to "+eff+" (ignoring): "+m);
+        }
         return newArgs.toArray(new Object[newArgs.size()]);
     }
 
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
index d88a500..76500bc 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
@@ -30,6 +30,7 @@
 import java.util.WeakHashMap;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.brooklyn.api.effector.Effector;
 import org.apache.brooklyn.api.entity.Application;
@@ -116,6 +117,7 @@
         return closed;
     }
 
+    private AtomicBoolean terminated = new AtomicBoolean(false);
     private String managementPlaneId;
     private String managementNodeId;
     private BasicExecutionManager execution;
@@ -316,15 +318,26 @@
     
     @Override
     public void terminate() {
-        INSTANCES.remove(this);
-        super.terminate();
-        if (osgiManager!=null) {
-            osgiManager.stop();
-            osgiManager = null;
+        synchronized (terminated) {
+            if (terminated.getAndSet(true)) {
+                log.trace("Already terminated management context "+this);
+                // no harm in doing it twice, but it makes logs ugly!
+                return;
+            }
+            log.debug("Terminating management context "+this);
+            
+            INSTANCES.remove(this);
+            super.terminate();
+            if (osgiManager!=null) {
+                osgiManager.stop();
+                osgiManager = null;
+            }
+            if (usageManager != null) usageManager.terminate();
+            if (execution != null) execution.shutdownNow();
+            if (gc != null) gc.shutdownNow();
+            
+            log.debug("Terminated management context "+this);
         }
-        if (usageManager != null) usageManager.terminate();
-        if (execution != null) execution.shutdownNow();
-        if (gc != null) gc.shutdownNow();
     }
 
     @Override
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 3b0795b..eb4ff10 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
@@ -55,6 +55,7 @@
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.javalang.AggregateClassLoader;
+import org.apache.brooklyn.util.javalang.Reflections;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -99,6 +100,7 @@
             interfaces.add(spec.getType());
         } else {
             log.warn("EntitySpec declared in terms of concrete type "+spec.getType()+"; should be supplied in terms of interface");
+            interfaces.addAll(Reflections.getAllInterfaces(spec.getType()));
         }
         interfaces.addAll(spec.getAdditionalInterfaces());
         
@@ -305,12 +307,16 @@
 
         /* Marked transient so that the task is not needlessly kept around at the highest level.
          * Note that the task is not normally visible in the GUI, because 
-         * (a) while it is running, the entity is parentless (and so not in the tree);
+         * (a) while it is running, the entity is often parentless (and so not in the tree);
          * and (b) when it is completed it is GC'd, as it is transient.
          * However task info is available via the API if you know its ID,
          * and if better subtask querying is available it will be picked up as a background task 
          * of the parent entity creating this child entity
          * (note however such subtasks are currently filtered based on parent entity so is excluded).
+         * <p>
+         * Some of these (initializers and enrichers) submit background scheduled tasks,
+         * which currently show up at the top level once the initializer task completes.
+         * TODO It would be nice if these schedule tasks were grouped in a bucket! 
          */
         ((EntityInternal)entity).getExecutionContext().submit(Tasks.builder().dynamic(false).displayName("Entity initialization")
                 .tag(BrooklynTaskTags.tagForContextEntity(entity))
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
index ac4bef5..0c622c3 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
@@ -248,7 +248,7 @@
 
             // return immediately if either the ready predicate or the abort conditions hold
             if (ready(value)) return postProcess(value);
-
+            
             final List<Exception> abortionExceptions = Lists.newCopyOnWriteArrayList();
             long start = System.currentTimeMillis();
             
@@ -790,6 +790,7 @@
                 .displayName("waiting on "+sensor.getName())
                 .description("Waiting on sensor "+sensor.getName()+" from "+source)
                 .tag("attributeWhenReady")
+                .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
                 .body(new WaitInTaskForAttributeReady<T,V>(this))
                 .build();
         }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
index 8f671f2..5ba36b3 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
@@ -103,7 +103,8 @@
                 @Override protected Object visitSpec() {
                     try { 
                         AbstractBrooklynObjectSpec<?, ?> result = createSpec(type, context);
-                        result.catalogItemId(type.getId());
+                        // see notes on catalogItemIdIfNotNull
+                        result.catalogItemIdIfNotNull(type.getId());
                         return result;
                     } catch (Exception e) { throw Exceptions.propagate(e); }
                 }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java
index 05f0773..e0e2305 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java
@@ -64,6 +64,7 @@
 
     @Override
     public String getId() {
+        if (symbolicName==null) return null;
         return symbolicName + (version!=null ? ":"+version : "");
     }
     
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroup.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroup.java
index aa9ca90..625d981 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroup.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroup.java
@@ -58,8 +58,12 @@
     AttributeSensor<Entity> FIRST = Sensors.newSensor(Entity.class,
             "cluster.first.entity", "The first member of the cluster");
 
+    /**
+     * @deprecated since 0.9.0, the UI no longer relies on the use of delegates to represent group membership (see #929)
+     */
+    @Deprecated
     ConfigKey<Boolean> MEMBER_DELEGATE_CHILDREN = ConfigKeys.newBooleanConfigKey(
-            "group.members.delegate", "Add delegate child entities for members of the group", Boolean.FALSE);
+            "group.members.delegate", "Deprecated: Add delegate child entities for members of the group", Boolean.FALSE);
 
     ConfigKey<String> MEMBER_DELEGATE_NAME_FORMAT = ConfigKeys.newStringConfigKey(
             "group.members.delegate.nameFormat", "Delegate members name format string (Use %s for the original entity display name)", "%s");
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroupImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroupImpl.java
index d8814bd..bfd366f 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroupImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroupImpl.java
@@ -146,6 +146,7 @@
                 sensors().emit(MEMBER_ADDED, member);
 
                 if (Boolean.TRUE.equals(getConfig(MEMBER_DELEGATE_CHILDREN))) {
+                    log.warn("Use of deprecated ConfigKey {} in {} (as of 0.9.0)", MEMBER_DELEGATE_CHILDREN.getName(), this);
                     Optional<Entity> result = Iterables.tryFind(getChildren(), Predicates.equalTo(member));
                     if (!result.isPresent()) {
                         String nameFormat = Optional.fromNullable(getConfig(MEMBER_DELEGATE_NAME_FORMAT)).or("%s");
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 f528db7..781cb0c 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
@@ -48,6 +48,7 @@
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Multimap;
 import com.google.common.reflect.TypeToken;
@@ -101,6 +102,15 @@
     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);
 
+    @SetFromFlag("quarantineFilter")
+    ConfigKey<Predicate<? super Throwable>> QUARANTINE_FILTER = ConfigKeys.newConfigKey(
+            new TypeToken<Predicate<? super Throwable>>() {},
+            "dynamiccluster.quarantineFilter", 
+            "Quarantine the failed nodes that pass this filter (given the exception thrown by the node). "
+                    + "Default is those that did not fail with NoMachinesAvailableException "
+                    + "(Config ignored if quarantineFailedEntities is false)", 
+            null);
+
     AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = Attributes.SERVICE_STATE_ACTUAL;
 
     BasicNotificationSensor<Entity> ENTITY_QUARANTINED = new BasicNotificationSensor<Entity>(Entity.class, "dynamiccluster.entityQuarantined", "Entity failed to start, and has been quarantined");
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 b8e5c63..f434fcf 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
@@ -21,6 +21,7 @@
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
@@ -38,6 +39,7 @@
 import org.apache.brooklyn.api.entity.Group;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
@@ -50,6 +52,7 @@
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ServiceProblemsLogic;
+import org.apache.brooklyn.core.entity.trait.Resizable;
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.entity.trait.StartableMethods;
 import org.apache.brooklyn.core.location.Locations;
@@ -80,6 +83,7 @@
 import com.google.common.base.Functions;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableList;
@@ -308,15 +312,16 @@
         setConfigEvenIfOwned(FACTORY, factory);
     }
 
-    private Location getLocation() {
+    private Location getLocation(boolean required) {
         Collection<? extends Location> ll = Locations.getLocationsCheckingAncestors(getLocations(), this);
-        try {
-            return Iterables.getOnlyElement(ll);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            if (ll.isEmpty()) throw new IllegalStateException("No location available for "+this);
-            else throw new IllegalStateException("Ambiguous location for "+this+"; expected one but had "+ll);
+        if (ll.isEmpty()) {
+            if (!required) return null;
+            throw new IllegalStateException("No location available for "+this);
         }
+        if (ll.size()>1) {
+            throw new IllegalStateException("Ambiguous location for "+this+"; expected one but had "+ll);
+        }
+        return Iterables.getOnlyElement(ll);
     }
 
     protected boolean isAvailabilityZoneEnabled() {
@@ -331,6 +336,19 @@
         return getAttribute(QUARANTINE_GROUP);
     }
 
+    protected Predicate<? super Throwable> getQuarantineFilter() {
+        Predicate<? super Throwable> result = getConfig(QUARANTINE_FILTER);
+        if (result != null) {
+            return result;
+        } else {
+            return new Predicate<Throwable>() {
+                @Override public boolean apply(Throwable input) {
+                    return Exceptions.getFirstThrowableOfType(input, NoMachinesAvailableException.class) == null;
+                }
+            };
+        }
+    }
+
     protected int getInitialQuorumSize() {
         int initialSize = getConfig(INITIAL_SIZE).intValue();
         int initialQuorumSize = getConfig(INITIAL_QUORUM_SIZE).intValue();
@@ -344,18 +362,17 @@
 
     @Override
     public void start(Collection<? extends Location> locsO) {
-        if (locsO!=null) {
-            checkArgument(locsO.size() <= 1, "Wrong number of locations supplied to start %s: %s", this, locsO);
-            addLocations(locsO);
-        }
-        Location loc = getLocation();
+        addLocations(locsO);
+        Location loc = getLocation(false);
 
         EntitySpec<?> spec = getConfig(MEMBER_SPEC);
         if (spec!=null) {
-            setDefaultDisplayName("Cluster of "+JavaClassNames.simpleClassName(spec.getType()) +" ("+loc+")");
+            setDefaultDisplayName("Cluster of "+JavaClassNames.simpleClassName(spec.getType()) +
+                (loc!=null ? " ("+loc+")" : ""));
         }
 
         if (isAvailabilityZoneEnabled()) {
+            if (loc==null) throw new IllegalStateException("When using availability zones, a location must be specified on the cluster");
             sensors().set(SUB_LOCATIONS, findSubLocations(loc));
         }
 
@@ -518,7 +535,20 @@
             } else {
                 if (LOG.isDebugEnabled()) LOG.debug("Resize no-op {} from {} to {}", new Object[] {this, originalSize, desiredSize});
             }
-            resizeByDelta(delta);
+            // If we managed to grow at all, then expect no exception.
+            // Otherwise, if failed because NoMachinesAvailable, then propagate as InsufficientCapacityException.
+            // This tells things like the AutoScalerPolicy to not keep retrying.
+            try {
+                resizeByDelta(delta);
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                NoMachinesAvailableException nmae = Exceptions.getFirstThrowableOfType(e, NoMachinesAvailableException.class);
+                if (nmae != null) {
+                    throw new Resizable.InsufficientCapacityException("Failed to resize", e);
+                } else {
+                    throw Exceptions.propagate(e);
+                }
+            }
         }
         return getCurrentSize();
     }
@@ -545,7 +575,7 @@
             Location memberLoc = null;
             if (isAvailabilityZoneEnabled()) {
                 // this member's location could be a machine provisioned by a sub-location, or the actual sub-location
-                List<Location> subLocations = findSubLocations(getLocation());
+                List<Location> subLocations = findSubLocations(getLocation(true));
                 Collection<Location> actualMemberLocs = member.getLocations();
                 boolean foundMatch = false;
                 for (Iterator<Location> iter = actualMemberLocs.iterator(); !foundMatch && iter.hasNext();) {
@@ -576,7 +606,7 @@
                 // Replacing member, so new member should be in the same location as that being replaced.
                 // Expect this to agree with `getMemberSpec().getLocations()` (if set). If not, then 
                 // presumably there was a reason this specific member was started somewhere else!
-                memberLoc = getLocation();
+                memberLoc = getLocation(false);
             }
 
             Entity replacement = replaceMember(member, memberLoc, ImmutableMap.of());
@@ -587,7 +617,7 @@
     /**
      * @throws StopFailedRuntimeException If stop failed, after successfully starting replacement
      */
-    protected Entity replaceMember(Entity member, Location memberLoc, Map<?, ?> extraFlags) {
+    protected Entity replaceMember(Entity member, @Nullable Location memberLoc, Map<?, ?> extraFlags) {
         synchronized (mutex) {
             ReferenceWithError<Optional<Entity>> added = addInSingleLocation(memberLoc, extraFlags);
 
@@ -624,7 +654,7 @@
     protected List<Location> getNonFailedSubLocations() {
         List<Location> result = Lists.newArrayList();
         Set<Location> failed = Sets.newLinkedHashSet();
-        List<Location> subLocations = findSubLocations(getLocation());
+        List<Location> subLocations = findSubLocations(getLocation(true));
         Set<Location> oldFailedSubLocations = getAttribute(FAILED_SUB_LOCATIONS);
         if (oldFailedSubLocations == null)
             oldFailedSubLocations = ImmutableSet.<Location> of();
@@ -669,7 +699,7 @@
         }
     }
 
-    /** <strong>Note</strong> for sub-clases; this method can be called while synchronized on {@link #mutex}. */
+    /** <strong>Note</strong> for sub-classes; this method can be called while synchronized on {@link #mutex}. */
     protected Collection<Entity> grow(int delta) {
         Preconditions.checkArgument(delta > 0, "Must call grow with positive delta.");
 
@@ -693,11 +723,13 @@
                         + ", when expected delta " + delta + " in " + this);
             }
         } else {
-            chosenLocations = Collections.nCopies(delta, getLocation());
+            chosenLocations = Collections.nCopies(delta, getLocation(false));
         }
 
-        // create and start the entities
-        return addInEachLocation(chosenLocations, ImmutableMap.of()).getWithError();
+        // create and start the entities.
+        // if any fail, then propagate the error.
+        ReferenceWithError<Collection<Entity>> result = addInEachLocation(chosenLocations, ImmutableMap.of());
+        return result.getWithError();
     }
 
     /** <strong>Note</strong> for sub-clases; this method can be called while synchronized on {@link #mutex}. */
@@ -728,8 +760,8 @@
         }
     }
 
-    protected ReferenceWithError<Optional<Entity>> addInSingleLocation(Location location, Map<?,?> flags) {
-        ReferenceWithError<Collection<Entity>> added = addInEachLocation(ImmutableList.of(location), flags);
+    protected ReferenceWithError<Optional<Entity>> addInSingleLocation(@Nullable Location location, Map<?,?> flags) {
+        ReferenceWithError<Collection<Entity>> added = addInEachLocation(Arrays.asList(location), flags);
         
         Optional<Entity> result = Iterables.isEmpty(added.getWithoutError()) ? Optional.<Entity>absent() : Optional.of(Iterables.getOnlyElement(added.get()));
         if (!added.hasError()) {
@@ -786,7 +818,7 @@
         // quarantine/cleanup as necessary
         if (!errors.isEmpty()) {
             if (isQuarantineEnabled()) {
-                quarantineFailedNodes(errors.keySet());
+                quarantineFailedNodes(errors);
             } else {
                 cleanupFailedNodes(errors.keySet());
             }
@@ -796,11 +828,18 @@
         return ReferenceWithError.newInstanceWithoutError(result);
     }
 
-    protected void quarantineFailedNodes(Collection<Entity> failedEntities) {
-        for (Entity entity : failedEntities) {
-            sensors().emit(ENTITY_QUARANTINED, entity);
-            getQuarantineGroup().addMember(entity);
-            removeMember(entity);
+    protected void quarantineFailedNodes(Map<Entity, Throwable> failedEntities) {
+        for (Map.Entry<Entity, Throwable> entry : failedEntities.entrySet()) {
+            Entity entity = entry.getKey();
+            Throwable cause = entry.getValue();
+            if (cause == null || getQuarantineFilter().apply(cause)) {
+                sensors().emit(ENTITY_QUARANTINED, entity);
+                getQuarantineGroup().addMember(entity);
+                removeMember(entity);
+            } else {
+                LOG.info("Cluster {} discarding failed node {}, rather than quarantining", this, entity);
+                discardNode(entity);
+            }
         }
     }
 
@@ -847,7 +886,7 @@
     }
 
     @Override
-    public Entity addNode(Location loc, Map<?, ?> extraFlags) {
+    public Entity addNode(@Nullable Location loc, Map<?, ?> extraFlags) {
         // In case subclasses are foolish and do not call super.init() when overriding.
         initialiseMemberId();
         Map<?, ?> createFlags = MutableMap.builder()
@@ -879,7 +918,9 @@
         if (memberSpec == null) memberSpec = getMemberSpec();
         
         if (memberSpec != null) {
-            return addChild(EntitySpec.create(memberSpec).configure(flags).location(loc));
+            EntitySpec<?> specConfigured = EntitySpec.create(memberSpec).configure(flags);
+            if (loc!=null) specConfigured.location(loc);
+            return addChild(specConfigured);
         }
 
         EntityFactory<?> factory = getFactory();
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java
index 67c4c79..34051c5 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java
@@ -41,6 +41,7 @@
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
 import org.apache.brooklyn.core.entity.trait.Changeable;
 import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.enricher.stock.Enrichers;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.exceptions.Exceptions;
@@ -109,15 +110,11 @@
     
     @Override
     public void start(Collection<? extends Location> locsO) {
-        if (locsO!=null) {
-            addLocations(locsO);
-        }
+        addLocations(locsO);
+        List<Location> locationsToStart = MutableList.copyOf(Locations.getLocationsCheckingAncestors(locsO, this));
         
-        List<Location> newLocations = MutableList.copyOf(locsO);
-        if (newLocations.isEmpty()) newLocations.addAll(getLocations());
-        
-        Preconditions.checkNotNull(newLocations, "locations must be supplied");
-        Preconditions.checkArgument(newLocations.size() >= 1, "One or more locations must be supplied");
+        Preconditions.checkNotNull(locationsToStart, "locations must be supplied");
+        Preconditions.checkArgument(locationsToStart.size() >= 1, "One or more locations must be supplied");
         
         int locIndex = 0;
         
@@ -133,8 +130,8 @@
                     Location it = null;
                     if (child.getLocations().isEmpty())
                         // give him any of these locations if he has none, allowing round robin here
-                        if (!newLocations.isEmpty()) {
-                            it = newLocations.get(locIndex++ % newLocations.size());
+                        if (!locationsToStart.isEmpty()) {
+                            it = locationsToStart.get(locIndex++ % locationsToStart.size());
                             ((EntityInternal)child).addLocations(Arrays.asList(it));
                         }
                     
@@ -144,12 +141,12 @@
                 }
             }
             // remove all the locations we applied to existing nodes
-            while (locIndex-->0 && !newLocations.isEmpty())
-                newLocations.remove(0);
+            while (locIndex-->0 && !locationsToStart.isEmpty())
+                locationsToStart.remove(0);
 
             // finally (and usually) we create new entities for locations passed in
             // (unless they were consumed by pre-existing children which didn't have locations)
-            for (Location it : newLocations) {
+            for (Location it : locationsToStart) {
                 Entity e = addCluster(it);
                 
                 ((EntityInternal)e).addLocations(Arrays.asList(it));
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/QuarantineGroup.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/QuarantineGroup.java
index e6f2aab..c13d914 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/QuarantineGroup.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/QuarantineGroup.java
@@ -27,8 +27,6 @@
 @ImplementedBy(QuarantineGroupImpl.class)
 public interface QuarantineGroup extends AbstractGroup {
 
-    ConfigKey<Boolean> MEMBER_DELEGATE_CHILDREN = ConfigKeys.newConfigKeyWithDefault(AbstractGroup.MEMBER_DELEGATE_CHILDREN, Boolean.TRUE);
-
     @Effector(description="Removes all members of the quarantined group, unmanaging them")
     void expungeMembers(
             @EffectorParam(name="firstStop", description="Whether to first call stop() on those members that are stoppable") boolean stopFirst);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
index b5a8616..54be703 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
@@ -48,11 +48,12 @@
 
     @Override
     public void start(Collection<? extends Location> locations) {
-        log.info("Starting entity "+this+" at "+locations);
         try {
             ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
 
             addLocations(locations);
+            locations = Locations.getLocationsCheckingAncestors(locations, this);
+            log.info("Starting entity "+this+" at "+locations);
 
             // essentially does StartableMethods.start(this, locations),
             // but optionally filters locations for each child
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java
index 9ee3b28..cc6d8d7 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java
@@ -38,6 +38,7 @@
 
     @Override
     public void start(Collection<? extends Location> locations) {
+        addLocations(locations);
         connectSensors();
         sensors().set(SERVICE_UP, Boolean.TRUE);
     }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java
index 5e74e7c..b059858 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java
@@ -89,6 +89,7 @@
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.exceptions.RuntimeInterruptedException;
 import org.apache.brooklyn.util.guava.KeyTransformingLoadingCache.KeyTransformingSameTypeLoadingCache;
+import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.pool.BasicPool;
 import org.apache.brooklyn.util.pool.Pool;
 import org.apache.brooklyn.util.ssh.BashCommands;
@@ -389,7 +390,7 @@
     private BasicPool<SshTool> buildPool(final Map<String, ?> properties) {
         return BasicPool.<SshTool>builder()
                 .name(getDisplayName()+"@"+address+":"+getPort()+
-                        (config().getRaw(SSH_HOST).isPresent() ? "("+getConfig(SSH_HOST)+":"+getConfig(SSH_PORT)+")" : "")+
+                        (config().getRaw(SSH_HOST).isPresent() ? "("+getConfig(SSH_HOST)+":"+getPort()+")" : "")+
                         ":hash"+System.identityHashCode(this))
                 .supplier(new Supplier<SshTool>() {
                         @Override public SshTool get() {
@@ -550,7 +551,21 @@
 
     /** port for SSHing */
     public int getPort() {
-        return getConfig(SshTool.PROP_PORT);
+        // Prefer PROP_PORT (i.e. "port"). However if that is explicitly null or is not set, then see if
+        // SSH_PORT (i.e. "brooklyn.ssh.config.port") has been set and use that.
+        // If neither is set (or is explicitly set to null), then use the default PROP_PORT value.
+        //
+        // Note we don't just rely on config().get(PROP_PORT) returning the default, because we hit a rebind
+        // error where the location's port configuration had been explicitly set to null.
+        // See https://issues.apache.org/jira/browse/BROOKLYN-215
+        
+        Maybe<Object> raw = config().getRaw(SshTool.PROP_PORT);
+        if (raw.orNull() == null && config().getRaw(SSH_PORT).orNull() != null) {
+            return config().get(SSH_PORT);
+        } else {
+            Integer result = config().get(SshTool.PROP_PORT);
+            return (result != null) ? result : SshTool.PROP_PORT.getDefaultValue();
+        }
     }
 
     protected <T> T execSsh(final Map<String, ?> props, final Function<ShellTool, T> task) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionContext.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionContext.java
index 13dda46..74e0ddd 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionContext.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionContext.java
@@ -175,18 +175,18 @@
         }
         
         final Object startCallback = properties.get("newTaskStartCallback");
-        properties.put("newTaskStartCallback", new Function<Object,Void>() {
-            public Void apply(Object it) {
+        properties.put("newTaskStartCallback", new Function<Task<?>,Void>() {
+            public Void apply(Task<?> it) {
                 registerPerThreadExecutionContext();
-                if (startCallback!=null) ExecutionUtils.invoke(startCallback, it);
+                if (startCallback!=null) BasicExecutionManager.invokeCallback(startCallback, it);
                 return null;
             }});
         
         final Object endCallback = properties.get("newTaskEndCallback");
-        properties.put("newTaskEndCallback", new Function<Object,Void>() {
-            public Void apply(Object it) {
+        properties.put("newTaskEndCallback", new Function<Task<?>,Void>() {
+            public Void apply(Task<?> it) {
                 try {
-                    if (endCallback!=null) ExecutionUtils.invoke(endCallback, it);
+                    if (endCallback!=null) BasicExecutionManager.invokeCallback(endCallback, it);
                 } finally {
                     clearPerThreadExecutionContext();
                 }
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 d90b1a1..0aab7d5 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
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.util.core.task;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import groovy.lang.Closure;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -51,16 +52,22 @@
 import org.apache.brooklyn.api.mgmt.TaskAdaptable;
 import org.apache.brooklyn.core.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.config.Sanitizer;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.core.task.TaskInternal.TaskCancellationMode;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.exceptions.RuntimeInterruptedException;
 import org.apache.brooklyn.util.text.Identifiers;
+import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
 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;
@@ -368,7 +375,6 @@
         return submitSubsequentScheduledTask(flags, task);
     }
     
-    @SuppressWarnings("unchecked")
     protected Task<?> submitSubsequentScheduledTask(final Map<?,?> flags, final ScheduledTask task) {
         if (!task.isDone()) {
             task.internalFuture = delayedRunner.schedule(new ScheduledTaskCallable(task, flags),
@@ -508,9 +514,15 @@
                      */
                     if (log.isDebugEnabled()) {
                         // debug only here, because most submitters will handle failures
-                        log.debug("Exception running task "+task+" (rethrowing): "+error.getMessage(), error);
-                        if (log.isTraceEnabled())
-                            log.trace("Trace for exception running task "+task+" (rethrowing): "+error.getMessage(), error);
+                        if (error instanceof InterruptedException || error instanceof RuntimeInterruptedException) {
+                            log.debug("Detected interruption on task "+task+" (rethrowing)" +
+                                (Strings.isNonBlank(error.getMessage()) ? ": "+error.getMessage() : ""));
+                        } else {
+                            log.debug("Exception running task "+task+" (rethrowing): "+error);
+                        }
+                        if (log.isTraceEnabled()) {
+                            log.trace("Trace for exception running task "+task+" (rethrowing): "+error, error);
+                        }
                     }
                     throw Exceptions.propagate(error);
                 }
@@ -526,19 +538,64 @@
         }
     }
 
-    private final static class ListenableForwardingFutureForTask<T> extends ListenableForwardingFuture<T> {
+    @SuppressWarnings("deprecation")
+    // TODO do we even need a listenable future here?  possibly if someone wants to interrogate the future it might
+    // be interesting, so possibly it is useful that we implement ListenableFuture...
+    private final static class CancellingListenableForwardingFutureForTask<T> extends ListenableForwardingFuture<T> {
         private final Task<T> task;
+        private BasicExecutionManager execMgmt;
 
-        private ListenableForwardingFutureForTask(Future<T> delegate, ExecutionList list, Task<T> task) {
+        private CancellingListenableForwardingFutureForTask(BasicExecutionManager execMgmt, Future<T> delegate, ExecutionList list, Task<T> task) {
             super(delegate, list);
+            this.execMgmt = execMgmt;
             this.task = task;
         }
 
         @Override
-        public boolean cancel(boolean mayInterruptIfRunning) {
+        public boolean cancel(TaskCancellationMode mode) {
             boolean result = false;
-            if (!task.isCancelled()) result |= task.cancel(mayInterruptIfRunning);
-            result |= super.cancel(mayInterruptIfRunning);
+            if (log.isTraceEnabled()) {
+                log.trace("CLFFT cancelling "+task+" mode "+mode);
+            }
+            if (!task.isCancelled()) result |= ((TaskInternal<T>)task).cancel(mode);
+            result |= delegate().cancel(mode.isAllowedToInterruptTask());
+            
+            if (mode.isAllowedToInterruptAllSubmittedTasks() || mode.isAllowedToInterruptDependentSubmittedTasks()) {
+                int subtasksFound=0;
+                int subtasksReallyCancelled=0;
+                
+                if (task instanceof HasTaskChildren) {
+                    for (Task<?> child: ((HasTaskChildren)task).getChildren()) {
+                        if (log.isTraceEnabled()) {
+                            log.trace("Cancelling "+child+" on recursive cancellation of "+task);
+                        }
+                        subtasksFound++;
+                        if (((TaskInternal<?>)child).cancel(mode)) {
+                            result = true;
+                            subtasksReallyCancelled++;
+                        }
+                    }
+                }
+                // TODO this is inefficient; might want to keep an index on submitted-by
+                for (Task<?> t: execMgmt.getAllTasks()) {
+                    if (task.equals(t.getSubmittedByTask())) {
+                        if (mode.isAllowedToInterruptAllSubmittedTasks() || BrooklynTaskTags.isTransient(t)) {
+                            if (log.isTraceEnabled()) {
+                                log.trace("Cancelling "+t+" on recursive cancellation of "+task);
+                            }
+                            subtasksFound++;
+                            if (((TaskInternal<?>)t).cancel(mode)) {
+                                result = true;
+                                subtasksReallyCancelled++;
+                            }
+                        }
+                    }
+                }
+                if (log.isTraceEnabled()) {
+                    log.trace("On cancel of "+task+", applicable subtask count "+subtasksFound+", of which "+subtasksReallyCancelled+" were actively cancelled");
+                }
+            }
+            
             ((TaskInternal<?>)task).runListeners();
             return result;
         }
@@ -571,9 +628,15 @@
 
     @SuppressWarnings("unchecked")
     protected <T> Task<T> submitNewTask(final Map<?,?> flags, final Task<T> task) {
-        if (log.isTraceEnabled()) log.trace("Submitting task {} ({}), with flags {}, and tags {}, job {}", 
+        if (log.isTraceEnabled()) {
+            log.trace("Submitting task {} ({}), with flags {}, and tags {}, job {}; caller {}", 
                 new Object[] {task.getId(), task, Sanitizer.sanitize(flags), task.getTags(), 
-                (task instanceof TaskInternal ? ((TaskInternal<T>)task).getJob() : "<unavailable>")});
+                (task instanceof TaskInternal ? ((TaskInternal<T>)task).getJob() : "<unavailable>"),
+                Tasks.current() });
+            if (Tasks.current()==null && BrooklynTaskTags.isTransient(task)) {
+                log.trace("Stack trace for unparented submission of transient "+task, new Throwable("trace only (not an error)"));
+            }
+        }
         
         if (task instanceof ScheduledTask)
             return (Task<T>) submitNewScheduledTask(flags, (ScheduledTask)task);
@@ -604,15 +667,16 @@
         } else {
             future = runner.submit(job);
         }
-        // on completion, listeners get triggered above; here, below we ensure they get triggered on cancel
-        // (and we make sure the same ExecutionList is used in the future as in the task)
-        ListenableFuture<T> listenableFuture = new ListenableForwardingFutureForTask<T>(future, ((TaskInternal<T>)task).getListeners(), task);
-        // doesn't matter whether the listener is added to the listenableFuture or the task,
-        // except that for the task we can more easily wrap it so that it only logs debug if the executor is shutdown
-        // (avoid a bunch of ugly warnings in tests which start and stop things a lot!)
-        // [probably even nicer to run this in the same thread, it doesn't do much; but that is messier to implement]
+        // SubmissionCallable (above) invokes the listeners on completion;
+        // this future allows a caller to add custom listeners
+        // (it does not notify the listeners; that's our job);
+        // except on cancel we want to listen
+        ListenableFuture<T> listenableFuture = new CancellingListenableForwardingFutureForTask<T>(this, future, ((TaskInternal<T>)task).getListeners(), task);
+        // and we want to make sure *our* (manager) listeners are given suitable callback 
         ((TaskInternal<T>)task).addListener(new SubmissionListenerToCallOtherListeners<T>(task), runner);
+        // NB: can the above mean multiple callbacks to TaskInternal#runListeners?
         
+        // finally expose the future to callers
         ((TaskInternal<T>)task).initInternalFuture(listenableFuture);
         
         return task;
@@ -665,9 +729,27 @@
             PerThreadCurrentTaskHolder.perThreadCurrentTask.set(task);
             ((TaskInternal<?>)task).setStartTimeUtc(System.currentTimeMillis());
         }
-        ExecutionUtils.invoke(flags.get("newTaskStartCallback"), task);
+        invokeCallback(flags.get("newTaskStartCallback"), task);
     }
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    // not ideal, such loose typing on the callback -- should prefer Function<Task,Object>
+    // but at least it's package-private
+    static Object invokeCallback(Object callable, Task<?> task) {
+        if (callable instanceof Closure) return ((Closure<?>)callable).call(task);
+        if (callable instanceof Callable) {
+            try {
+                return ((Callable<?>)callable).call();
+            } catch (Throwable t) {
+                throw Exceptions.propagate(t);
+            }
+        }
+        if (callable instanceof Runnable) { ((Runnable)callable).run(); return null; }
+        if (callable instanceof Function) { return ((Function)callable).apply(task); }
+        if (callable==null) return null;
+        throw new IllegalArgumentException("Cannot invoke unexpected callback object "+callable+" of type "+callable.getClass()+" on "+task);
+    }
+    
     /** normally (if not interrupted) called once for each call to {@link #beforeSubmitScheduledTaskAllIterations(Map, Task)} */
     protected void afterEndScheduledTaskAllIterations(Map<?,?> flags, Task<?> task) {
         internalAfterEnd(flags, task, false, true);
@@ -693,7 +775,7 @@
         }
         if (isEndingAllIterations) {
             incompleteTaskIds.remove(task.getId());
-            ExecutionUtils.invoke(flags.get("newTaskEndCallback"), task);
+            invokeCallback(flags.get("newTaskEndCallback"), task);
             ((TaskInternal<?>)task).setEndTimeUtc(System.currentTimeMillis());
         }
 
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 0c26dd1..7c29bba 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
@@ -155,7 +155,7 @@
             (Strings.isNonEmpty(displayName) ? 
                 displayName : 
                 (job + (tags!=null && !tags.isEmpty() ? ";"+tags : "")) ) +
-            ":"+getId()+"]";
+            "]@"+getId();
     }
 
     @Override
@@ -196,7 +196,7 @@
     protected Maybe<Task<?>> submittedByTask;
 
     protected volatile Thread thread = null;
-    private volatile boolean cancelled = false;
+    protected volatile boolean cancelled = false;
     /** normally a {@link ListenableFuture}, except for scheduled tasks when it may be a {@link ScheduledFuture} */
     protected volatile Future<T> internalFuture = null;
     
@@ -288,15 +288,34 @@
     }
     
     @Override
-    public synchronized boolean cancel(boolean mayInterruptIfRunning) {
+    public final synchronized boolean cancel(boolean mayInterruptIfRunning) {
+        // semantics changed in 2016-01, previously "true" was INTERRUPT_TASK_BUT_NOT_SUBMITTED_TASKS
+        return cancel(mayInterruptIfRunning ? TaskCancellationMode.INTERRUPT_TASK_AND_DEPENDENT_SUBMITTED_TASKS
+            : TaskCancellationMode.DO_NOT_INTERRUPT);
+    }
+    
+    @Override @Beta
+    public synchronized boolean cancel(TaskCancellationMode mode) {
         if (isDone()) return false;
-        boolean cancel = true;
-        cancelled = true;
-        if (internalFuture!=null) { 
-            cancel = internalFuture.cancel(mayInterruptIfRunning);
+        if (log.isTraceEnabled()) {
+            log.trace("BT cancelling "+this+" mode "+mode);
         }
+        cancelled = true;
+        doCancel(mode);
         notifyAll();
-        return cancel;
+        return true;
+    }
+    
+    @SuppressWarnings("deprecation")
+    protected boolean doCancel(TaskCancellationMode mode) {
+        if (internalFuture!=null) { 
+            if (internalFuture instanceof ListenableForwardingFuture) {
+                return ((ListenableForwardingFuture<?>)internalFuture).cancel(mode);
+            } else {
+                return internalFuture.cancel(mode.isAllowedToInterruptTask());
+            }
+        }
+        return true;
     }
 
     @Override
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/DynamicSequentialTask.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/DynamicSequentialTask.java
index b7985c8..51a4e34 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/DynamicSequentialTask.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/DynamicSequentialTask.java
@@ -158,27 +158,44 @@
     }
 
     @Override
-    public boolean cancel(boolean mayInterruptIfRunning) {
-        return cancel(mayInterruptIfRunning, mayInterruptIfRunning, true);
-    }
-    public boolean cancel(boolean mayInterruptTask, boolean interruptPrimaryThread, boolean alsoCancelChildren) {
-        if (isDone()) return false;
-        if (log.isTraceEnabled()) log.trace("cancelling {}", this);
-        boolean cancel = super.cancel(mayInterruptTask);
-        if (alsoCancelChildren) {
+    protected boolean doCancel(TaskCancellationMode mode) {
+        boolean result = false;
+        if (mode.isAllowedToInterruptDependentSubmittedTasks() || mode.isAllowedToInterruptAllSubmittedTasks()) {
             for (Task<?> t: secondaryJobsAll)
-                cancel |= t.cancel(mayInterruptTask);
+                result = ((TaskInternal<?>)t).cancel(mode) || result;
         }
+        return super.doCancel(mode) || result;
+        // returns true if anything is successfully cancelled
+    }
+    
+    public boolean cancel(TaskCancellationMode mode) {
+        return cancel(mode, null);
+    }
+    
+    protected boolean cancel(TaskCancellationMode mode, Boolean interruptPrimaryThreadOverride) {
+        if (isDone()) return false;
+        if (log.isTraceEnabled()) log.trace("cancelling DST {}", this);
+        
+        // first do the super's cancel, setting cancelled, and calling doCancel to cancel children
+        boolean result = super.cancel(mode);
+        // then come back and ensure our primary thread is cancelled if needed
+        
+        if (interruptPrimaryThreadOverride==null) interruptPrimaryThreadOverride = mode.isAllowedToInterruptTask();
+        if (log.isTraceEnabled()) {
+            log.trace("DST cancelling "+this+" mode "+mode+", interruptPrimary "+interruptPrimaryThreadOverride);
+        }
+
         synchronized (jobTransitionLock) {
             if (primaryThread!=null) {
-                if (interruptPrimaryThread) {
+                if (interruptPrimaryThreadOverride) {
                     if (log.isTraceEnabled()) log.trace("cancelling {} - interrupting", this);
                     primaryThread.interrupt();
                 }
-                cancel = true;
+                result = true;
             }
         }
-        return cancel;
+        
+        return result;
     }
     
     @Override
@@ -309,7 +326,7 @@
                                         }
 
                                         if (!primaryFinished && failureHandlingConfig.cancelPrimaryOnSecondaryFailure) {
-                                            cancel(true, false, false);
+                                            cancel(TaskCancellationMode.INTERRUPT_TASK_BUT_NOT_SUBMITTED_TASKS, false);
                                         }
                                         
                                         result.add(Tasks.getError(secondaryJob));
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ExecutionUtils.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ExecutionUtils.java
deleted file mode 100644
index 72a5ae4..0000000
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ExecutionUtils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.util.core.task;
-
-import groovy.lang.Closure;
-
-import java.util.concurrent.Callable;
-
-import com.google.common.base.Function;
-import com.google.common.base.Throwables;
-
-public class ExecutionUtils {
-    /**
-     * Attempts to run/call the given object, with the given arguments if possible, preserving the return value if there is one (null otherwise);
-     * throws exception if the callable is a non-null object which cannot be invoked (not a callable or runnable)
-     * @deprecated since 0.7.0 ; this super-loose typing should be avoided; if it is needed, let's move it to one of the Groovy compatibility classes
-     */
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static Object invoke(Object callable, Object ...args) {
-        if (callable instanceof Closure) return ((Closure<?>)callable).call(args);
-        if (callable instanceof Callable) {
-            try {
-                return ((Callable<?>)callable).call();
-            } catch (Throwable t) {
-                throw Throwables.propagate(t);
-            }
-        }
-        if (callable instanceof Runnable) { ((Runnable)callable).run(); return null; }
-        if (callable instanceof Function && args.length == 1) { return ((Function)callable).apply(args[0]); }
-        if (callable==null) return null;
-        throw new IllegalArgumentException("Cannot invoke unexpected object "+callable+" of type "+callable.getClass()+", with "+args.length+" args");
-    }
-}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ListenableForwardingFuture.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ListenableForwardingFuture.java
index 4ce56d1..cbc474c 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ListenableForwardingFuture.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ListenableForwardingFuture.java
@@ -21,15 +21,26 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.Future;
 
+import org.apache.brooklyn.util.core.task.TaskInternal.TaskCancellationMode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.google.common.util.concurrent.ExecutionList;
 import com.google.common.util.concurrent.ForwardingFuture.SimpleForwardingFuture;
 import com.google.common.util.concurrent.ListenableFuture;
 
-/** Wraps a Future, making it a ListenableForwardingFuture, but with the caller having the resposibility to:
+/** Wraps a Future, making it a ListenableForwardingFuture, but with the caller having the responsibility to:
  * <li> invoke the listeners on job completion (success or error)
- * <li> invoke the listeners on cancel */
+ * <li> invoke the listeners on cancel
+ * 
+ * @deprecated since 0.9.0 likely to leave the public API */
+@Deprecated  // TODO just one subclass, it can hold the behaviour we need from this, 
+// and the methods here are surprising as they expect the caller to notify the list
 public abstract class ListenableForwardingFuture<T> extends SimpleForwardingFuture<T> implements ListenableFuture<T> {
 
+    private static final Logger log = LoggerFactory.getLogger(ListenableForwardingFuture.class);
+    
+    // TODO these are never accessed or used
     final ExecutionList listeners;
     
     protected ListenableForwardingFuture(Future<T> delegate) {
@@ -42,9 +53,22 @@
         this.listeners = list;
     }
 
+    private static boolean warned = false;
+    
     @Override
     public void addListener(Runnable listener, Executor executor) {
+        if (!warned) {
+            log.warn("Use of deprecated ListenableForwardingFuture.addListener at "+this+" (future calls will not be logged)", new Throwable("stack trace"));
+            warned = true;
+        }
+        
         listeners.add(listener, executor);
     }
     
+    public abstract boolean cancel(TaskCancellationMode mode);
+    
+    public final boolean cancel(boolean mayInterrupt) {
+        return cancel(TaskCancellationMode.INTERRUPT_TASK_AND_DEPENDENT_SUBMITTED_TASKS);
+    }
+    
 }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ScheduledTask.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ScheduledTask.java
index c1ad4f8..219f4f8 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ScheduledTask.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ScheduledTask.java
@@ -43,7 +43,7 @@
  */
 // TODO ScheduledTask is a very pragmatic implementation; would be nice to tighten, 
 // reduce external assumptions about internal structure, and clarify "done" semantics
-public class ScheduledTask extends BasicTask {
+public class ScheduledTask extends BasicTask<Object> {
     
     final Callable<Task<?>> taskFactory;
 
@@ -84,7 +84,7 @@
         this(MutableMap.of(), task);
     }
 
-    public ScheduledTask(Map flags, final Task<?> task){
+    public ScheduledTask(Map<?,?> flags, final Task<?> task){
         this(flags, new Callable<Task<?>>(){
             @Override
             public Task<?> call() throws Exception {
@@ -92,7 +92,7 @@
             }});
     }
 
-    public ScheduledTask(Map flags, Callable<Task<?>> taskFactory) {
+    public ScheduledTask(Map<?,?> flags, Callable<Task<?>> taskFactory) {
         super(flags);
         this.taskFactory = taskFactory;
         
@@ -194,13 +194,11 @@
     }
     
     @Override
-    public synchronized boolean cancel(boolean mayInterrupt) {
-        boolean result = super.cancel(mayInterrupt);
+    protected boolean doCancel(org.apache.brooklyn.util.core.task.TaskInternal.TaskCancellationMode mode) {
         if (nextRun!=null) {
-            nextRun.cancel(mayInterrupt);
-            notifyAll();
+            ((TaskInternal<?>)nextRun).cancel(mode);
         }
-        return result;
+        return super.doCancel(mode);
     }
     
     /**
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskInternal.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskInternal.java
index 2bf0fec..99c2773 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskInternal.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskInternal.java
@@ -29,6 +29,7 @@
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.util.concurrent.ExecutionList;
 import com.google.common.util.concurrent.ListenableFuture;
 
@@ -95,6 +96,8 @@
     
     Object getExtraStatusText();
 
+    /** On task completion (or cancellation) runs the listeners which have been registered using 
+     * {@link #addListener(Runnable, java.util.concurrent.Executor)}. */
     void runListeners();
 
     void setEndTimeUtc(long val);
@@ -120,5 +123,41 @@
     /** if a task is a proxy for another one (used mainly for internal tasks),
      * this returns the "real" task represented by this one */
     Task<?> getProxyTarget();
+
+    /** clearer semantics around cancellation; may be promoted to {@link Task} if we  */
+    @Beta
+    public boolean cancel(TaskCancellationMode mode);
+    
+    @Beta
+    public static class TaskCancellationMode {
+        public static final TaskCancellationMode DO_NOT_INTERRUPT = new TaskCancellationMode(false, false, false);
+        public static final TaskCancellationMode INTERRUPT_TASK_BUT_NOT_SUBMITTED_TASKS = new TaskCancellationMode(true, false, false);
+        public static final TaskCancellationMode INTERRUPT_TASK_AND_DEPENDENT_SUBMITTED_TASKS = new TaskCancellationMode(true, true, false);
+        public static final TaskCancellationMode INTERRUPT_TASK_AND_ALL_SUBMITTED_TASKS = new TaskCancellationMode(true, true, true);
+        
+        private final boolean allowedToInterruptTask, 
+            allowedToInterruptDependentSubmittedTasks, 
+            allowedToInterruptAllSubmittedTasks;
+        
+        private TaskCancellationMode(boolean mayInterruptIfRunning, boolean interruptSubmittedTransients, boolean interruptAllSubmitted) {
+            this.allowedToInterruptTask = mayInterruptIfRunning;
+            this.allowedToInterruptDependentSubmittedTasks = interruptSubmittedTransients;
+            this.allowedToInterruptAllSubmittedTasks = interruptAllSubmitted;
+        }
+        
+        public boolean isAllowedToInterruptTask() { return allowedToInterruptTask; }
+        /** Implementation-dependent what "dependent" means in this context, 
+         * e.g. may be linked to a "transient" tag (that's what Brooklyn does) */ 
+        public boolean isAllowedToInterruptDependentSubmittedTasks() { return allowedToInterruptDependentSubmittedTasks; }
+        public boolean isAllowedToInterruptAllSubmittedTasks() { return allowedToInterruptAllSubmittedTasks; }
+        
+        @Override
+        public String toString() {
+            return Objects.toStringHelper(this).add("interruptTask", allowedToInterruptTask)
+                .add("interruptDependentSubmitted", allowedToInterruptDependentSubmittedTasks)
+                .add("interruptAllSubmitted", allowedToInterruptAllSubmittedTasks)
+                .toString();
+        }
+    }
     
 }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskPredicates.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskPredicates.java
index 8e46002..d8d3764 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskPredicates.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskPredicates.java
@@ -60,4 +60,20 @@
             return "displayNameMatches("+matcher+")";
         }
     }
+    
+    public static Predicate<Task<?>> isDone() {
+        return new IsDone();
+    }
+    
+    private static class IsDone implements Predicate<Task<?>> {
+        @Override
+        public boolean apply(Task<?> input) {
+            return input.isDone();
+        }
+        @Override
+        public String toString() {
+            return "isDone()";
+        }
+    }
+    
 }
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntity.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntity.java
index 532eb4d..7845326 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntity.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntity.java
@@ -60,7 +60,7 @@
     ConfigKey<Predicate<? super FailingEntity>> FAIL_ON_RESTART_CONDITION = (ConfigKey) ConfigKeys.newConfigKey(Predicate.class, "failOnRestartCondition", "Whether to throw exception on call to restart", null);
     
     @SetFromFlag("exceptionClazz")
-    ConfigKey<Class<? extends RuntimeException>> EXCEPTION_CLAZZ = (ConfigKey) ConfigKeys.newConfigKey(Class.class, "exceptionClazz", "Type of exception to throw", IllegalStateException.class);
+    ConfigKey<Class<? extends Exception>> EXCEPTION_CLAZZ = (ConfigKey) ConfigKeys.newConfigKey(Class.class, "exceptionClazz", "Type of exception to throw", IllegalStateException.class);
     
     @SetFromFlag("execOnFailure")
     ConfigKey<Function<? super FailingEntity,?>> EXEC_ON_FAILURE = (ConfigKey) ConfigKeys.newConfigKey(Function.class, "execOnFailure", "Callback to execute before throwing an exception, on any failure", Functions.identity());
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java
index a675c78..423bdd3 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java
@@ -79,7 +79,12 @@
     
     private RuntimeException newException(String msg) {
         try {
-            return getConfig(EXCEPTION_CLAZZ).getConstructor(String.class).newInstance("Simulating entity stop failure for test");
+            Exception result = getConfig(EXCEPTION_CLAZZ).getConstructor(String.class).newInstance("Simulating entity stop failure for test");
+            if (!(result instanceof RuntimeException)) {
+                return new RuntimeException("wrapping", result);
+            } else {
+                return (RuntimeException)result;
+            }
         } catch (Exception e) {
             throw Exceptions.propagate(e);
         }
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindSshMachineLocationTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindSshMachineLocationTest.java
index 92fdb55..ec8cacb 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindSshMachineLocationTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindSshMachineLocationTest.java
@@ -80,5 +80,23 @@
         
         assertEquals(newChildLoc.execScript(Collections.<String,Object>emptyMap(), "mysummary", ImmutableList.of("true")), 0);
     }
-    
+
+    // See https://issues.apache.org/jira/browse/BROOKLYN-215
+    @Test(groups="Integration")
+    public void testRebindWhenPortNull() throws Exception {
+        SshMachineLocation machine = origManagementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+                .configure("address", "localhost")
+                .configure("port", null));
+        FixedListMachineProvisioningLocation<?> byon = origManagementContext.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+                .configure("machines", ImmutableList.of(machine)));
+        origApp.start(ImmutableList.of(byon));
+        LOG.info("Before rebind, machine="+machine.toString());
+
+        newApp = (TestApplication) rebind();
+        
+        FixedListMachineProvisioningLocation<?> newByon = (FixedListMachineProvisioningLocation<?>) Iterables.getOnlyElement(newApp.getLocations(), 0);
+        SshMachineLocation newMachine = (SshMachineLocation) Iterables.get(newByon.getChildren(), 0);
+        
+        LOG.info("After rebind, machine="+newMachine.toString());
+    }
 }
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestCluster.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestCluster.java
index 3ff61f0..9593203 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestCluster.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestCluster.java
@@ -18,8 +18,12 @@
  */
 package org.apache.brooklyn.core.test.entity;
 
+import java.util.List;
+
 import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.entity.group.DynamicCluster;
 
 /**
@@ -27,4 +31,10 @@
 */
 @ImplementedBy(TestClusterImpl.class)
 public interface TestCluster extends DynamicCluster, EntityLocal {
+    
+    ConfigKey<Integer> MAX_SIZE = ConfigKeys.newIntegerConfigKey("testCluster.maxSize", "Size after which it will throw InsufficientCapacityException", Integer.MAX_VALUE);
+    
+    List<Integer> getSizeHistory();
+    
+    List<Integer> getDesiredSizeHistory();
 }
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestClusterImpl.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestClusterImpl.java
index bf8d0cb..0edea8f 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestClusterImpl.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestClusterImpl.java
@@ -18,22 +18,32 @@
  */
 package org.apache.brooklyn.core.test.entity;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.entity.group.DynamicClusterImpl;
 import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks;
 
+import com.google.common.collect.ImmutableList;
+
 /**
 * Mock cluster entity for testing.
 */
 public class TestClusterImpl extends DynamicClusterImpl implements TestCluster {
     private volatile int size;
 
+    private final List<Integer> desiredSizeHistory = Collections.synchronizedList(new ArrayList<Integer>());
+    private final List<Integer> sizeHistory = Collections.synchronizedList(new ArrayList<Integer>());
+    
     public TestClusterImpl() {
     }
     
     @Override
     public void init() {
         super.init();
+        sizeHistory.add(size);
         size = getConfig(INITIAL_SIZE);
         sensors().set(Startable.SERVICE_UP, true);
     }
@@ -48,11 +58,36 @@
     
     @Override
     public Integer resize(Integer desiredSize) {
-        this.size = desiredSize;
+        desiredSizeHistory.add(desiredSize);
+        int achievableSize = Math.min(desiredSize, getConfig(MAX_SIZE));
+        
+        if (achievableSize != size) {
+            this.sizeHistory.add(achievableSize);
+            this.size = achievableSize;
+        }
+        
+        if (desiredSize > achievableSize) {
+            throw new InsufficientCapacityException("Simulating insufficient capacity (desiredSize="+desiredSize+"; maxSize="+getConfig(MAX_SIZE)+"; newSize="+size+")");
+        }
+
         return size;
     }
     
     @Override
+    public List<Integer> getSizeHistory() {
+        synchronized (sizeHistory) {
+            return ImmutableList.copyOf(sizeHistory);
+        }
+    }
+    
+    @Override
+    public List<Integer> getDesiredSizeHistory() {
+        synchronized (desiredSizeHistory) {
+            return ImmutableList.copyOf(desiredSizeHistory);
+        }
+    }
+    
+    @Override
     public void stop() {
         size = 0;
         super.stop();
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java
index dd37e58..b58c630 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java
@@ -44,6 +44,7 @@
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.sensor.SensorEvent;
 import org.apache.brooklyn.core.entity.Attributes;
@@ -54,6 +55,7 @@
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
 import org.apache.brooklyn.core.entity.trait.Changeable;
 import org.apache.brooklyn.core.entity.trait.FailingEntity;
+import org.apache.brooklyn.core.entity.trait.Resizable;
 import org.apache.brooklyn.core.location.SimulatedLocation;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
@@ -72,13 +74,13 @@
 import org.testng.annotations.Test;
 
 import com.google.common.base.Function;
+import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.Atomics;
 
 
@@ -108,9 +110,9 @@
         try {
             app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
                     .configure("factory", "error"));
-            fail();
+            Asserts.shouldHaveFailedPreviously();
         } catch (Exception e) {
-            if (Exceptions.getFirstThrowableOfType(e, IllegalArgumentException.class) == null) throw e;
+            Asserts.expectedFailure(e);
         }
     }
 
@@ -119,9 +121,9 @@
         DynamicCluster c = app.createAndManageChild(EntitySpec.create(DynamicCluster.class));
         try {
             c.start(ImmutableList.of(loc));
-            fail();
+            Asserts.shouldHaveFailedPreviously();
         } catch (Exception e) {
-            if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) throw e;
+            Asserts.expectedFailureOfType(e, IllegalStateException.class);
         }
     }
     
@@ -133,9 +135,9 @@
             cluster.start(ImmutableList.of(loc));
             cluster.stop();
             cluster.start(ImmutableList.of(loc2));
-            fail();
+            Asserts.shouldHaveFailedPreviously();
         } catch (Exception e) {
-            if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) throw e;
+            Asserts.expectedFailureOfType(e, IllegalStateException.class);
         }
     }
 
@@ -145,9 +147,9 @@
                 .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(TestEntity.class)));
         try {
             cluster.start(ImmutableList.of(loc, loc2));
-            fail();
+            Asserts.shouldHaveFailedPreviously();
         } catch (Exception e) {
-            if (Exceptions.getFirstThrowableOfType(e, IllegalArgumentException.class) == null) throw e;
+            Asserts.expectedFailureContainsIgnoreCase(e, "ambiguous");
         }
     }
 
@@ -202,6 +204,65 @@
         assertEquals(entity.getApplication(), app);
     }
 
+    @Test
+    public void testResizeWhereChildThrowsNoMachineAvailableExceptionIsPropagatedAsInsufficientCapacityException() throws Exception {
+        final DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
+            .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(FailingEntity.class)
+                    .configure(FailingEntity.FAIL_ON_START, true)
+                    .configure(FailingEntity.EXCEPTION_CLAZZ, NoMachinesAvailableException.class))
+            .configure(DynamicCluster.INITIAL_SIZE, 0));
+        cluster.start(ImmutableList.of(loc));
+        
+        try {
+            cluster.resize(1);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            Asserts.expectedFailureOfType(e, Resizable.InsufficientCapacityException.class);
+        }
+    }
+
+    @Test
+    public void testResizeWhereSubsetOfChildrenThrowsNoMachineAvailableExceptionIsPropagatedAsInsuffientCapacityException() throws Exception {
+        final DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
+            .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(FailingEntity.class)
+                    .configure(FailingEntity.FAIL_ON_START_CONDITION, new Predicate<FailingEntity>() {
+                        final AtomicInteger counter = new AtomicInteger();
+                        @Override public boolean apply(FailingEntity input) {
+                            // Only second and subsequent entities fail
+                            int index = counter.getAndIncrement();
+                            return (index >= 1);
+                        }})
+                    .configure(FailingEntity.EXCEPTION_CLAZZ, NoMachinesAvailableException.class))
+            .configure(DynamicCluster.INITIAL_SIZE, 0));
+        cluster.start(ImmutableList.of(loc));
+
+        // Managed to partially resize, but will still throw exception.
+        // The getCurrentSize will report how big we managed to get.
+        // The children that failed due to NoMachinesAvailableException will have been unmanaged automatically.
+        try {
+            cluster.resize(2);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            Asserts.expectedFailureOfType(e, Resizable.InsufficientCapacityException.class);
+        }
+        assertEquals(cluster.getCurrentSize(), (Integer)1);
+        Iterable<FailingEntity> children1 = Iterables.filter(cluster.getChildren(), FailingEntity.class);
+        assertEquals(Iterables.size(children1), 1);
+        assertEquals(Iterables.getOnlyElement(children1).sensors().get(TestEntity.SERVICE_UP), Boolean.TRUE);
+        
+        // This attempt will also fail, because all new children will fail
+        try {
+            cluster.resize(2);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            Asserts.expectedFailureOfType(e, Resizable.InsufficientCapacityException.class);
+        }
+        assertEquals(cluster.getCurrentSize(), (Integer)1);
+        Iterable<FailingEntity> children2 = Iterables.filter(cluster.getChildren(), FailingEntity.class);
+        assertEquals(Iterables.size(children2), 1);
+        assertEquals(Iterables.getOnlyElement(children2), Iterables.getOnlyElement(children1));
+    }
+
     /** This can be sensitive to order, e.g. if TestEntity set expected RUNNING before setting SERVICE_UP, 
      * there would be a point when TestEntity is ON_FIRE.
      * <p>
@@ -249,6 +310,7 @@
         assertEquals(Iterables.size(Entities.descendants(cluster, TestEntity.class)), 0);
     }
 
+    
     @Test
     public void currentSizePropertyReflectsActualClusterSize() throws Exception {
         DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
@@ -583,6 +645,62 @@
     }
 
     @Test
+    public void testQuarantineFailedEntitiesRespectsCustomFilter() throws Exception {
+        Predicate<Throwable> filter = new Predicate<Throwable>() {
+            @Override public boolean apply(Throwable input) {
+                return Exceptions.getFirstThrowableOfType(input, AllowedException.class) != null;
+            }
+        };
+        runQuarantineFailedEntitiesRespectsFilter(AllowedException.class, DisallowedException.class, filter);
+    }
+    @SuppressWarnings("serial")
+    public static class AllowedException extends RuntimeException {
+        public AllowedException(String message) {
+            super(message);
+        }
+    }
+    @SuppressWarnings("serial")
+    public static class DisallowedException extends RuntimeException {
+        public DisallowedException(String message) {
+            super(message);
+        }
+    }
+
+    @Test
+    public void testQuarantineFailedEntitiesRespectsDefaultFilter() throws Exception {
+        Predicate<Throwable> filter = null;
+        runQuarantineFailedEntitiesRespectsFilter(AllowedException.class, NoMachinesAvailableException.class, filter);
+    }
+    
+    protected void runQuarantineFailedEntitiesRespectsFilter(Class<? extends Exception> allowedException, 
+            Class<? extends Exception> disallowedException, Predicate<Throwable> quarantineFilter) throws Exception {
+        final List<Class<? extends Exception>> failureCauses = ImmutableList.<Class<? extends Exception>>of(allowedException, disallowedException);
+        final AtomicInteger counter = new AtomicInteger(0);
+        DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
+                .configure("quarantineFailedEntities", true)
+                .configure("initialSize", 0)
+                .configure("quarantineFilter", quarantineFilter)
+                .configure("factory", new EntityFactory() {
+                    @Override public Entity newEntity(Map flags, Entity parent) {
+                        int num = counter.getAndIncrement();
+                        return app.getManagementContext().getEntityManager().createEntity(EntitySpec.create(FailingEntity.class)
+                                .configure(flags)
+                                .configure(FailingEntity.FAIL_ON_START, true)
+                                .configure(FailingEntity.EXCEPTION_CLAZZ, failureCauses.get(num))
+                                .parent(parent));
+                    }}));
+
+        cluster.start(ImmutableList.of(loc));
+        resizeExpectingError(cluster, 2);
+        Iterable<FailingEntity> children = Iterables.filter(cluster.getChildren(), FailingEntity.class);
+        Collection<Entity> quarantineMembers = cluster.sensors().get(DynamicCluster.QUARANTINE_GROUP).getMembers();
+        
+        assertEquals(cluster.getCurrentSize(), (Integer)0);
+        assertEquals(Iterables.getOnlyElement(children).config().get(FailingEntity.EXCEPTION_CLAZZ), allowedException);
+        assertEquals(Iterables.getOnlyElement(quarantineMembers), Iterables.getOnlyElement(children));
+    }
+
+    @Test
     public void defaultRemovalStrategyShutsDownNewestFirstWhenResizing() throws Exception {
         final List<Entity> creationOrder = Lists.newArrayList();
         DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/BasicTaskExecutionPerformanceTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/BasicTaskExecutionPerformanceTest.java
index a291c53..e8e7890 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/BasicTaskExecutionPerformanceTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/BasicTaskExecutionPerformanceTest.java
@@ -70,7 +70,6 @@
         if (em != null) em.shutdownNow();
     }
     
-    @SuppressWarnings("unchecked")
     @Test
     public void testScheduledTaskExecutedAfterDelay() throws Exception {
         int delay = 100;
@@ -95,7 +94,6 @@
         assertTrue(actualDelay < (delay+MAX_OVERHEAD_MS), "actualDelay="+actualDelay+"; delay="+delay);
     }
 
-    @SuppressWarnings("unchecked")
     @Test
     public void testScheduledTaskExecutedAtRegularPeriod() throws Exception {
         final int period = 100;
@@ -127,7 +125,6 @@
         }
     }
 
-    @SuppressWarnings("unchecked")
     @Test
     public void testCanCancelScheduledTask() throws Exception {
         final int period = 1;
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 ceff29f..364870a 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
@@ -30,10 +30,13 @@
 
 import org.apache.brooklyn.api.mgmt.HasTaskChildren;
 import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.collections.CollectionFunctionals;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+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;
@@ -49,6 +52,8 @@
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.base.Stopwatch;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -154,7 +159,17 @@
     }
     
     public Task<String> sayTask(String message, Duration duration, String message2) {
-        return Tasks.<String>builder().body(sayCallable(message, duration, message2)).build();
+        return Tasks.<String>builder().displayName("say:"+message).body(sayCallable(message, duration, message2)).build();
+    }
+    
+    public <T> Task<T> submitting(final Task<T> task) {
+        return Tasks.<T>builder().displayName("submitting:"+task.getId()).body(new Callable<T>() {
+            @Override
+            public T call() throws Exception {
+                ec.submit(task);
+                return task.get();
+            }
+        }).build();
     }
     
     @Test
@@ -207,6 +222,85 @@
         // but we do _not_ get a mutex from task3 as it does not run (is not interrupted)
         Assert.assertEquals(cancellations.availablePermits(), 0);
     }
+    
+    @Test
+    public void testCancellationModeAndSubmitted() throws Exception {
+        doTestCancellationModeAndSubmitted(true, TaskCancellationMode.DO_NOT_INTERRUPT, false, false);
+        
+        doTestCancellationModeAndSubmitted(true, TaskCancellationMode.INTERRUPT_TASK_AND_ALL_SUBMITTED_TASKS, true, true);
+        doTestCancellationModeAndSubmitted(true, TaskCancellationMode.INTERRUPT_TASK_AND_DEPENDENT_SUBMITTED_TASKS, true, true);
+        doTestCancellationModeAndSubmitted(true, TaskCancellationMode.INTERRUPT_TASK_BUT_NOT_SUBMITTED_TASKS, true, false);
+        
+        // if it's not transient, it should only be cancelled on "all submitted"
+        doTestCancellationModeAndSubmitted(false, TaskCancellationMode.INTERRUPT_TASK_AND_DEPENDENT_SUBMITTED_TASKS, true, false);
+        doTestCancellationModeAndSubmitted(false, TaskCancellationMode.INTERRUPT_TASK_AND_ALL_SUBMITTED_TASKS, true, true);
+        
+        // cancellation mode left off should be the same as TASK_AND_DEPENDENT, i.e. don't cancel non-transient bg submitted
+        doTestCancellationModeAndSubmitted(true, null, true, true);
+        doTestCancellationModeAndSubmitted(false, null, true, false);
+        // and 'true' should be the same
+        doTestCancellationModeAndSubmitted(true, true, true, true);
+        doTestCancellationModeAndSubmitted(false, true, true, false);
+        
+        // cancellation mode false should be the same as DO_NOT_INTERRUPT
+        doTestCancellationModeAndSubmitted(true, false, false, false);
+    }
+    
+    public void doTestCancellationModeAndSubmitted(
+            boolean isSubtaskTransient,
+            Object cancellationMode,
+            boolean expectedTaskInterrupted,
+            boolean expectedSubtaskCancelled
+            ) throws Exception {
+        tearDown(); setUp();
+        
+        final Task<String> t1 = sayTask("1-wait", Duration.minutes(10), "1-done");
+        if (isSubtaskTransient) {
+            BrooklynTaskTags.addTagDynamically(t1, BrooklynTaskTags.TRANSIENT_TASK_TAG);
+        }
+        
+        final Task<List<?>> t = Tasks.parallel(
+                submitting(t1),
+                sayTask("2-wait", Duration.minutes(10), "2-done"));
+        ec.submit(t);
+        
+        waitForMessages(Predicates.compose(MathPredicates.greaterThanOrEqual(2), CollectionFunctionals.sizeFunction()), TIMEOUT);
+        Asserts.assertEquals(MutableSet.copyOf(messages), MutableSet.of("1-wait", "2-wait"));
+
+        if (cancellationMode==null) {
+            ((TaskInternal<?>)t).cancel();
+        } else if (cancellationMode instanceof Boolean) {
+            t.cancel((Boolean)cancellationMode);
+        } else if (cancellationMode instanceof TaskCancellationMode) {
+            ((TaskInternal<?>)t).cancel((TaskCancellationMode)cancellationMode);
+        } else {
+            throw new IllegalStateException("Invalid cancellationMode: "+cancellationMode);
+        }
+
+        // the cancelled task always reports cancelled and done
+        Assert.assertEquals(t.isDone(), true);
+        Assert.assertEquals(t.isCancelled(), true);
+        // end time might not be set for another fraction of a second
+        if (expectedTaskInterrupted) { 
+            Asserts.eventually(new Supplier<Number>() {
+                @Override public Number get() { return t.getEndTimeUtc(); }}, 
+                MathPredicates.<Number>greaterThanOrEqual(0));
+        } else {
+            Assert.assertTrue(t.getEndTimeUtc() < 0, "Wrong end time: "+t.getEndTimeUtc());
+        }
+        
+        if (expectedSubtaskCancelled) {
+            Asserts.eventually(Suppliers.ofInstance(t1), TaskPredicates.isDone());
+            Assert.assertTrue(t1.isCancelled());
+            Asserts.eventually(new Supplier<Number>() {
+                @Override public Number get() { return t1.getEndTimeUtc(); }}, 
+                MathPredicates.<Number>greaterThanOrEqual(0));
+        } else {
+            Time.sleep(Duration.millis(5));
+            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();
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/NonBasicTaskExecutionTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/NonBasicTaskExecutionTest.java
index 1d1c3af..6fd3021 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/NonBasicTaskExecutionTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/NonBasicTaskExecutionTest.java
@@ -64,6 +64,11 @@
         protected TaskInternal<T> delegate() {
             return delegate;
         }
+
+        @Override
+        public boolean cancel(TaskCancellationMode mode) {
+            return delegate.cancel(mode);
+        }
     }
     
     private BasicExecutionManager em;
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/ScheduledExecutionTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/ScheduledExecutionTest.java
index 1d551e8..5c11355 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/ScheduledExecutionTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/ScheduledExecutionTest.java
@@ -45,7 +45,7 @@
 import com.google.common.base.Stopwatch;
 import com.google.common.collect.Lists;
 
-@SuppressWarnings({"unchecked","rawtypes"})
+@SuppressWarnings({"rawtypes"})
 public class ScheduledExecutionTest {
 
     public static final Logger log = LoggerFactory.getLogger(ScheduledExecutionTest.class);
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/TaskPredicatesTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/TaskPredicatesTest.java
index fce6f0f..8a25361 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/TaskPredicatesTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/TaskPredicatesTest.java
@@ -57,8 +57,8 @@
                 .body(Callables.<Object>returning("val"))
                 .displayName("myname")
                 .build());
-        assertTrue(TaskPredicates.displayNameMatches(Predicates.equalTo("myname")).apply(task));
-        assertFalse(TaskPredicates.displayNameMatches(Predicates.equalTo("wrong")).apply(task));
+        assertTrue(TaskPredicates.displayNameSatisfies(Predicates.equalTo("myname")).apply(task));
+        assertFalse(TaskPredicates.displayNameSatisfies(Predicates.equalTo("wrong")).apply(task));
     }
     
     @Test
diff --git a/brooklyn-server/karaf/features/src/main/feature/feature.xml b/brooklyn-server/karaf/features/src/main/feature/feature.xml
index 384bc7d..49a90e6 100644
--- a/brooklyn-server/karaf/features/src/main/feature/feature.xml
+++ b/brooklyn-server/karaf/features/src/main/feature/feature.xml
@@ -197,4 +197,22 @@
         <!--<feature version="${project.version}">brooklyn-core</feature>-->
     </feature>
   
+    <feature name="brooklyn-software-winrm" version="${project.version}" description="Brooklyn WinRM Software Entities">
+        <bundle>mvn:org.apache.brooklyn/brooklyn-software-winrm/${project.version}</bundle>
+        <feature>brooklyn-core</feature>
+        <bundle dependency="true">wrap:mvn:io.cloudsoft.windows/winrm4j/${winrm4j.version}</bundle>
+    </feature>
+
+    <feature name="brooklyn-policy"  version="${project.version}" description="Brooklyn Policies">
+        <bundle>mvn:org.apache.brooklyn/brooklyn-policy/${project.version}</bundle>
+        <feature>brooklyn-core</feature>
+    </feature>
+
+    <feature name="brooklyn-software-base"  version="${project.version}"  description="Brooklyn Software Base">
+        <bundle>mvn:org.apache.brooklyn/brooklyn-software-base/${project.version}</bundle>
+        <feature>brooklyn-software-winrm</feature>
+        <feature>brooklyn-policy</feature>
+        <!-- python dependency required but not supported since karaf cannot handle its runtime bindings -->
+    </feature>
+
 </features>
diff --git a/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java b/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
index 20897d9..05972a2 100644
--- a/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
@@ -862,7 +862,7 @@
                         String scriptContent = ResourceUtils.create(this).getResourceAsString(setupScriptItem);
                         String script = TemplateProcessor.processTemplateContents(scriptContent, getManagementContext(), substitutions);
                         if (windows) {
-                            ((WinRmMachineLocation)machineLocation).executeScript(ImmutableList.copyOf((script.replace("\r", "").split("\n"))));
+                            ((WinRmMachineLocation)machineLocation).executeCommand(ImmutableList.copyOf((script.replace("\r", "").split("\n"))));
                         } else {
                             ((SshMachineLocation)machineLocation).execCommands("Customizing node " + this, ImmutableList.of(script));
                         }
@@ -2664,7 +2664,7 @@
                 public Boolean call() {
                     for (Map.Entry<WinRmMachineLocation, LoginCredentials> entry : machinesToTry.entrySet()) {
                         WinRmMachineLocation machine = entry.getKey();
-                        WinRmToolResponse response = machine.executeScript(
+                        WinRmToolResponse response = machine.executeCommand(
                                 ImmutableMap.of(WinRmTool.PROP_EXEC_TRIES.getName(), 1),
                                 ImmutableList.of("echo testing"));
                         boolean success = (response.getStatusCode() == 0);
diff --git a/brooklyn-server/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java b/brooklyn-server/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
index d315260..b484359 100644
--- a/brooklyn-server/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
+++ b/brooklyn-server/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
@@ -20,7 +20,6 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.brooklyn.util.JavaGroovyEquivalents.groovyTruth;
-import groovy.lang.Closure;
 
 import java.util.Map;
 import java.util.concurrent.Callable;
@@ -30,8 +29,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntityLocal;
@@ -55,6 +52,8 @@
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
@@ -62,6 +61,8 @@
 import com.google.common.reflect.TypeToken;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 
+import groovy.lang.Closure;
+
 
 /**
  * Policy that is attached to a {@link Resizable} entity and dynamically adjusts its size in response to
@@ -290,6 +291,7 @@
             .defaultValue(1)
             .reconfigurable(true)
             .build();
+    
     @SetFromFlag("resizeUpIterationMax")
     public static final ConfigKey<Integer> RESIZE_UP_ITERATION_MAX = BasicConfigKey.builder(Integer.class)
             .name("autoscaler.resizeUpIterationMax")
@@ -297,6 +299,7 @@
             .description("Maximum change to the size on a single iteration when scaling up")
             .reconfigurable(true)
             .build();
+    
     @SetFromFlag("resizeDownIterationIncrement")
     public static final ConfigKey<Integer> RESIZE_DOWN_ITERATION_INCREMENT = BasicConfigKey.builder(Integer.class)
             .name("autoscaler.resizeDownIterationIncrement")
@@ -304,6 +307,7 @@
             .defaultValue(1)
             .reconfigurable(true)
             .build();
+    
     @SetFromFlag("resizeDownIterationMax")
     public static final ConfigKey<Integer> RESIZE_DOWN_ITERATION_MAX = BasicConfigKey.builder(Integer.class)
             .name("autoscaler.resizeDownIterationMax")
@@ -346,6 +350,12 @@
             .reconfigurable(true)
             .build();
 
+    public static final ConfigKey<Integer> INSUFFICIENT_CAPACITY_HIGH_WATER_MARK = BasicConfigKey.builder(Integer.class)
+            .name("autoscaler.insufficientCapacityHighWaterMark")
+            .defaultValue(null)
+            .reconfigurable(true)
+            .build();
+
     @SetFromFlag("resizeOperator")
     public static final ConfigKey<ResizeOperator> RESIZE_OPERATOR = BasicConfigKey.builder(ResizeOperator.class)
             .name("autoscaler.resizeOperator")
@@ -564,6 +574,10 @@
         return getConfig(MAX_POOL_SIZE);
     }
     
+    private Integer getInsufficientCapacityHighWaterMark() {
+        return getConfig(INSUFFICIENT_CAPACITY_HIGH_WATER_MARK);
+    }
+    
     private ResizeOperator getResizeOperator() {
         return getConfig(RESIZE_OPERATOR);
     }
@@ -620,6 +634,14 @@
                 throw new IllegalArgumentException("Min pool size "+val+" must not be greater than max pool size "+getConfig(MAX_POOL_SIZE));
             }
             onPoolSizeLimitsChanged(getConfig(MIN_POOL_SIZE), newMax);
+        } else if (key.equals(INSUFFICIENT_CAPACITY_HIGH_WATER_MARK)) {
+            Integer newVal = (Integer) val;
+            Integer oldVal = config().get(INSUFFICIENT_CAPACITY_HIGH_WATER_MARK);
+            if (oldVal != null && (newVal == null || newVal > oldVal)) {
+                LOG.info("{} resetting {} to {}, which will enable resizing above previous level of {}",
+                        new Object[] {AutoScalerPolicy.this, INSUFFICIENT_CAPACITY_HIGH_WATER_MARK.getName(), newVal, oldVal});
+                // TODO see above about changing metricLowerBound; not triggering resize now
+            }
         } else {
             throw new UnsupportedOperationException("reconfiguring "+key+" unsupported for "+this);
         }
@@ -848,9 +870,17 @@
         onNewUnboundedPoolSize(desiredSizeUnconstrained);
     }
 
+    private int applyMinMaxConstraints(long desiredSize) {
+        return applyMinMaxConstraints(desiredSize > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)desiredSize);
+    }
+    
     private int applyMinMaxConstraints(int desiredSize) {
-        desiredSize = Math.max(getMinPoolSize(), desiredSize);
-        desiredSize = Math.min(getMaxPoolSize(), desiredSize);
+        int minSize = getMinPoolSize();
+        int maxSize = getMaxPoolSize();
+        Integer insufficientCapacityHighWaterMark = getInsufficientCapacityHighWaterMark();
+        desiredSize = Math.max(minSize, desiredSize);
+        desiredSize = Math.min(maxSize, desiredSize);
+        if (insufficientCapacityHighWaterMark != null) desiredSize = Math.min(insufficientCapacityHighWaterMark, desiredSize);
         return desiredSize;
     }
 
@@ -989,36 +1019,47 @@
     }
 
     private void resizeNow() {
-        long currentPoolSize = getCurrentSizeOperator().apply(poolEntity);
+        final int currentPoolSize = getCurrentSizeOperator().apply(poolEntity);
         CalculatedDesiredPoolSize calculatedDesiredPoolSize = calculateDesiredPoolSize(currentPoolSize);
-        final long desiredPoolSize = calculatedDesiredPoolSize.size;
+        long desiredPoolSize = calculatedDesiredPoolSize.size;
         boolean stable = calculatedDesiredPoolSize.stable;
         
+        final int targetPoolSize = applyMinMaxConstraints(desiredPoolSize);
+        
         if (!stable) {
             // the desired size fluctuations are not stable; ensure we check again later (due to time-window)
             // even if no additional events have been received
             // (note we continue now with as "good" a resize as we can given the instability)
             if (LOG.isTraceEnabled()) LOG.trace("{} re-scheduling resize check for {}, as desired size not stable (current {}, desired {}); continuing with resize...", 
-                    new Object[] {this, poolEntity, currentPoolSize, desiredPoolSize});
+                    new Object[] {this, poolEntity, currentPoolSize, targetPoolSize});
             scheduleResize();
         }
-        if (currentPoolSize == desiredPoolSize) {
+        if (currentPoolSize == targetPoolSize) {
             if (LOG.isTraceEnabled()) LOG.trace("{} not resizing pool {} from {} to {}", 
-                    new Object[] {this, poolEntity, currentPoolSize, desiredPoolSize});
+                    new Object[] {this, poolEntity, currentPoolSize, targetPoolSize});
             return;
         }
         
         if (LOG.isDebugEnabled()) LOG.debug("{} requesting resize to {}; current {}, min {}, max {}", 
-                new Object[] {this, desiredPoolSize, currentPoolSize, getMinPoolSize(), getMaxPoolSize()});
+                new Object[] {this, targetPoolSize, currentPoolSize, getMinPoolSize(), getMaxPoolSize()});
         
         Entities.submit(entity, Tasks.<Void>builder().displayName("Auto-scaler")
-            .description("Auto-scaler recommending resize from "+currentPoolSize+" to "+desiredPoolSize)
+            .description("Auto-scaler recommending resize from "+currentPoolSize+" to "+targetPoolSize)
             .tag(BrooklynTaskTags.NON_TRANSIENT_TASK_TAG)
             .body(new Callable<Void>() {
                 @Override
                 public Void call() throws Exception {
                     // TODO Should we use int throughout, rather than casting here?
-                    getResizeOperator().resize(poolEntity, (int) desiredPoolSize);
+                    try {
+                        getResizeOperator().resize(poolEntity, (int) targetPoolSize);
+                    } catch (Resizable.InsufficientCapacityException e) {
+                        // cannot resize beyond this; set the high-water mark
+                        int insufficientCapacityHighWaterMark = getCurrentSizeOperator().apply(poolEntity);
+                        LOG.warn("{} failed to resize {} due to insufficient capacity; setting high-water mark to {}, "
+                                + "and will not attempt to resize above that level again", 
+                                new Object[] {AutoScalerPolicy.this, poolEntity, insufficientCapacityHighWaterMark});
+                        config().set(INSUFFICIENT_CAPACITY_HIGH_WATER_MARK, insufficientCapacityHighWaterMark);
+                    }
                     return null;
                 }
             }).build())
diff --git a/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyMetricTest.java b/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyMetricTest.java
index e04f714..ad67b75 100644
--- a/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyMetricTest.java
+++ b/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyMetricTest.java
@@ -36,17 +36,18 @@
 import org.apache.brooklyn.core.test.entity.TestCluster;
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.time.Duration;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 
 public class AutoScalerPolicyMetricTest {
     
-    private static long TIMEOUT_MS = 10000;
-    private static long SHORT_WAIT_MS = 50;
+    private static long SHORT_WAIT_MS = 250;
     
     private static final AttributeSensor<Integer> MY_ATTRIBUTE = Sensors.newIntegerSensor("autoscaler.test.intAttrib");
     TestApplication app;
@@ -75,7 +76,7 @@
         Asserts.succeedsContinually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 1));
 
         tc.sensors().set(MY_ATTRIBUTE, 101);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 2));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
     }
     
     @Test
@@ -89,7 +90,7 @@
         Asserts.succeedsContinually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 2));
 
         tc.sensors().set(MY_ATTRIBUTE, 49);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 1));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 1));
     }
     
     @Test(groups="Integration")
@@ -101,11 +102,11 @@
         
         // workload 200 so requires doubling size to 10 to handle: (200*5)/100 = 10
         tc.sensors().set(MY_ATTRIBUTE, 200);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 10));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 10));
         
         // workload 5, requires 1 entity: (10*110)/100 = 11
         tc.sensors().set(MY_ATTRIBUTE, 110);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 11));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 11));
     }
     
     @Test(groups="Integration")
@@ -117,14 +118,14 @@
         
         // workload can be handled by 4 servers, within its valid range: (49*5)/50 = 4.9
         tc.sensors().set(MY_ATTRIBUTE, 49);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 4));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 4));
         
         // workload can be handled by 4 servers, within its valid range: (25*4)/50 = 2
         tc.sensors().set(MY_ATTRIBUTE, 25);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 2));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
         
         tc.sensors().set(MY_ATTRIBUTE, 0);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 1));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 1));
     }
     
     @Test(groups="Integration")
@@ -139,11 +140,11 @@
 
         // Decreases to min-size only
         tc.sensors().set(MY_ATTRIBUTE, 0);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 2));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
         
         // Increases to max-size only
         tc.sensors().set(MY_ATTRIBUTE, 100000);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 6));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 6));
     }
     
     @Test(groups="Integration",invocationCount=20)
@@ -167,14 +168,14 @@
 
         // workload can be handled by 6 servers, so no need to notify: 6 <= (100*6)/50
         tc.sensors().set(MY_ATTRIBUTE, 600);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 6));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 6));
         assertTrue(maxReachedEvents.isEmpty());
         
         // Increases to above max capacity: would require (100000*6)/100 = 6000
         tc.sensors().set(MY_ATTRIBUTE, 100000);
         
         // Assert our listener gets notified (once)
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+        Asserts.succeedsEventually(new Runnable() {
             public void run() {
                 assertEquals(maxReachedEvents.size(), 1);
                 assertEquals(maxReachedEvents.get(0).getMaxAllowed(), 6);
@@ -245,7 +246,7 @@
         policy.suspend();
         policy.resume();
         tc.sensors().set(MY_ATTRIBUTE, 101);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 2));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
     }
     
     @Test
@@ -268,6 +269,84 @@
 
         // Then confirm we listen to the correct "entityWithMetric"
         entityWithMetric.sensors().set(TestEntity.SEQUENCE, 101);
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 2));
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
+    }
+    
+    @Test(groups="Integration")
+    public void testOnFailedGrowWillSetHighwaterMarkAndNotResizeAboveThatAgain() {
+        tc = app.createAndManageChild(EntitySpec.create(TestCluster.class)
+                .configure("initialSize", 0)
+                .configure(TestCluster.MAX_SIZE, 2));
+
+        tc.resize(1);
+        
+        tc.policies().add(AutoScalerPolicy.builder()
+                .metric(MY_ATTRIBUTE)
+                .metricLowerBound(50)
+                .metricUpperBound(100)
+                .buildSpec());
+        
+        // workload 200 so requires doubling size to 2 to handle: (200*1)/100 = 2
+        tc.sensors().set(MY_ATTRIBUTE, 200);
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
+        assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2), "desired="+tc.getDesiredSizeHistory());
+        assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2), "sizes="+tc.getSizeHistory());
+        tc.sensors().set(MY_ATTRIBUTE, 100);
+        
+        // workload 110, requires 1 more entity: (2*110)/100 = 2.1
+        // But max size is 2, so resize will fail.
+        tc.sensors().set(MY_ATTRIBUTE, 110);
+        Asserts.succeedsContinually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 2));
+        assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2, 3), "desired="+tc.getDesiredSizeHistory()); // TODO succeeds eventually?
+        assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2), "sizes="+tc.getSizeHistory());
+        
+        // another attempt to resize will not cause it to ask
+        tc.sensors().set(MY_ATTRIBUTE, 110);
+        Asserts.succeedsContinually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 2));
+        assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2, 3), "desired="+tc.getDesiredSizeHistory());
+        assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2), "sizes="+tc.getSizeHistory());
+        
+        // but if we shrink down again, then we'll still be able to go up to the previous level.
+        // We'll only try to go as high as the previous high-water mark though.
+        tc.sensors().set(MY_ATTRIBUTE, 1);
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 1));
+        assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2, 3, 1), "desired="+tc.getDesiredSizeHistory());
+        assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2, 1), "sizes="+tc.getSizeHistory());
+        
+        tc.sensors().set(MY_ATTRIBUTE, 10000);
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 2));
+        assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2, 3, 1, 2), "desired="+tc.getDesiredSizeHistory());
+        assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2, 1, 2), "sizes="+tc.getSizeHistory());
+    }
+    
+    // When there is a resizeUpStabilizationDelay, it remembers all the previously requested sizes (in the recent history)
+    // and then looks at those in the stabilization-delay to determine the sustained desired. This test checks that
+    // we apply the highwater-mark even when the desired size had been recorded prior to the highwater mark being
+    // discovered.
+    @Test(groups="Integration")
+    public void testOnFailedGrowWithStabilizationDelayWillSetHighwaterMarkAndNotResizeAboveThatAgain() throws Exception {
+        tc = app.createAndManageChild(EntitySpec.create(TestCluster.class)
+                .configure("initialSize", 0)
+                .configure(TestCluster.MAX_SIZE, 2));
+
+        tc.resize(1);
+        
+        tc.policies().add(AutoScalerPolicy.builder()
+                .metric(MY_ATTRIBUTE)
+                .metricLowerBound(50)
+                .metricUpperBound(100)
+                .resizeUpStabilizationDelay(Duration.ONE_SECOND)
+                .buildSpec());
+        
+        // workload 200 so requires doubling size to 2 to handle: (200*1)/100 = 2
+        for (int i = 0; i < 10; i++) {
+            tc.sensors().set(MY_ATTRIBUTE, 200 + (i*100));
+            Thread.sleep(100);
+        }
+        
+        Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
+        Asserts.succeedsContinually(currentSizeAsserter(tc, 2));
+        assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2, 3), "desired="+tc.getDesiredSizeHistory());
+        assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2), "sizes="+tc.getSizeHistory());
     }
 }
diff --git a/brooklyn-server/pom.xml b/brooklyn-server/pom.xml
index 59bf6b2..4e6012b 100644
--- a/brooklyn-server/pom.xml
+++ b/brooklyn-server/pom.xml
@@ -90,7 +90,7 @@
         <coverage.target>${working.dir}</coverage.target>
 
 		<!-- Dependency Versions -->
-        <jclouds.version>1.9.1</jclouds.version> <!-- JCLOUDS_VERSION -->
+        <jclouds.version>1.9.2</jclouds.version> <!-- JCLOUDS_VERSION -->
         <logback.version>1.0.7</logback.version>
         <slf4j.version>1.6.6</slf4j.version>  <!-- used for java.util.logging jul-to-slf4j interception -->
         <guava.version>17.0</guava.version>
@@ -146,7 +146,7 @@
         <jsr311-api.version>1.1.1</jsr311-api.version>
         <maxmind.version>0.8.1</maxmind.version>
         <jna.version>4.0.0</jna.version>
-        <winrm4j.version>0.1.0</winrm4j.version>
+        <winrm4j.version>0.2.0</winrm4j.version>
         
         <!-- Transitive dependencies, declared explicitly to avoid version mismatch -->
         <clojure.version>1.4.0</clojure.version>
diff --git a/brooklyn-server/rest/rest-client/src/main/java/org/apache/brooklyn/rest/client/util/http/BuiltResponsePreservingError.java b/brooklyn-server/rest/rest-client/src/main/java/org/apache/brooklyn/rest/client/util/http/BuiltResponsePreservingError.java
index fb43c4c..d011172 100644
--- a/brooklyn-server/rest/rest-client/src/main/java/org/apache/brooklyn/rest/client/util/http/BuiltResponsePreservingError.java
+++ b/brooklyn-server/rest/rest-client/src/main/java/org/apache/brooklyn/rest/client/util/http/BuiltResponsePreservingError.java
@@ -70,7 +70,9 @@
     
     @Override
     public Object getEntity() {
-        if (error!=null) Exceptions.propagate(error);
+        if (error!=null) {
+            throw new IllegalStateException("getEntity called on BuiltResponsePreservingError, where an Error had been preserved", error);
+        }
         return super.getEntity();
     }
 
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
index 01bf992..82bac22 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
@@ -124,7 +124,12 @@
         Map<String,Object> result = MutableMap.of();
         
         for (CatalogItem<?,?> item: items) {
-            result.put(item.getId(), CatalogTransformer.catalogItemSummary(brooklyn(), item));
+            try {
+                result.put(item.getId(), CatalogTransformer.catalogItemSummary(brooklyn(), item));
+            } catch (Throwable t) {
+                log.warn("Error loading catalog item '"+item+"' (rethrowing): "+t);
+                throw Exceptions.propagate(t);
+            }
         }
         return Response.status(Status.CREATED).entity(result).build();
     }
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
index e483d0b..a6d3b8e 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
@@ -22,6 +22,7 @@
 
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Callable;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.config.ConfigKey;
@@ -35,6 +36,7 @@
 import org.apache.brooklyn.rest.transform.EntityTransformer;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 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.text.Strings;
 import org.apache.brooklyn.util.time.Duration;
@@ -71,14 +73,32 @@
     public Map<String, Object> batchConfigRead(String application, String entityToken, Boolean raw) {
         // TODO: add test
         Entity entity = brooklyn().getEntity(application, entityToken);
-        Map<ConfigKey<?>, ?> source = ((EntityInternal) entity).config().getBag().getAllConfigAsConfigKeyMap();
-        Map<String, Object> result = Maps.newLinkedHashMap();
-        for (Map.Entry<ConfigKey<?>, ?> ek : source.entrySet()) {
-            Object value = ek.getValue();
-            result.put(ek.getKey().getName(), 
-                resolving(value).preferJson(true).asJerseyOutermostReturnValue(false).raw(raw).context(entity).timeout(Duration.ZERO).renderAs(ek.getKey()).resolve());
+        // wrap in a task for better runtime view
+        return Entities.submit(entity, Tasks.<Map<String,Object>>builder().displayName("REST API batch config read").body(new BatchConfigRead(this, entity, raw)).build()).getUnchecked();
+    }
+    
+    private static class BatchConfigRead implements Callable<Map<String,Object>> {
+        private EntityConfigResource resource;
+        private Entity entity;
+        private Boolean raw;
+
+        public BatchConfigRead(EntityConfigResource resource, Entity entity, Boolean raw) {
+            this.resource = resource;
+            this.entity = entity;
+            this.raw = raw;
         }
-        return result;
+
+        @Override
+        public Map<String, Object> call() throws Exception {
+            Map<ConfigKey<?>, ?> source = ((EntityInternal) entity).config().getBag().getAllConfigAsConfigKeyMap();
+            Map<String, Object> result = Maps.newLinkedHashMap();
+            for (Map.Entry<ConfigKey<?>, ?> ek : source.entrySet()) {
+                Object value = ek.getValue();
+                result.put(ek.getKey().getName(), 
+                    resource.resolving(value).preferJson(true).asJerseyOutermostReturnValue(false).raw(raw).context(entity).timeout(Duration.ZERO).renderAs(ek.getKey()).resolve());
+            }
+            return result;
+        }
     }
 
     @Override
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
index a9fa4dc..c029bd3 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
@@ -218,6 +218,7 @@
                         if (shutdownHandler != null) {
                             shutdownHandler.onShutdownRequest();
                         } else {
+                            // should normally be set, as @Context is required by jersey injection
                             log.warn("ShutdownHandler not set, exiting process");
                             System.exit(0);
                         }
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
index 38a38eb..978755b 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
@@ -93,9 +93,12 @@
             }
         }
         
-        Builder rb = ApiError.builderFromThrowable(Exceptions.collapse(throwable2));
+        Throwable throwable3 = Exceptions.collapse(throwable2);
+        Builder rb = ApiError.builderFromThrowable(throwable3);
         if (Strings.isBlank(rb.getMessage()))
             rb.message("Internal error. Contact server administrator to consult logs for more details.");
+        if (Exceptions.isPrefixImportant(throwable3))
+            rb.message(Exceptions.getPrefixText(throwable3)+": "+rb.getMessage());
         return rb.build().asResponse(Status.INTERNAL_SERVER_ERROR, MediaType.APPLICATION_JSON_TYPE);
     }
 }
diff --git a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
index f641267..92ac8f7 100644
--- a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
+++ b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
@@ -48,8 +48,8 @@
 import org.apache.brooklyn.rest.security.provider.SecurityProvider;
 import org.apache.brooklyn.rest.util.ManagementContextProvider;
 import org.apache.brooklyn.rest.util.OsgiCompat;
+import org.apache.brooklyn.rest.util.ServerStoppingShutdownHandler;
 import org.apache.brooklyn.rest.util.ShutdownHandlerProvider;
-import org.apache.brooklyn.rest.util.TestShutdownHandler;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.net.Networking;
@@ -94,7 +94,7 @@
     public static final String SCANNING_CATALOG_BOM_URL = "classpath://brooklyn/scanning.catalog.bom";
 
     enum StartMode {
-        FILTER, SERVLET, WEB_XML
+        FILTER, SERVLET, /** web-xml is not fully supported */ @Beta WEB_XML
     }
 
     public static final List<Class<? extends Filter>> DEFAULT_FILTERS = ImmutableList.of(
@@ -112,7 +112,7 @@
     private ContextHandler customContext;
     private boolean deployJsgui = true;
     private boolean disableHighAvailability = true;
-    private final TestShutdownHandler shutdownListener = new TestShutdownHandler();
+    private ServerStoppingShutdownHandler shutdownListener;
 
     protected BrooklynRestApiLauncher() {}
 
@@ -215,7 +215,12 @@
             ((BrooklynProperties) mgmt.getConfig()).put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, ManagementContextInternal.EMPTY_CATALOG_URL);
         }
 
-        return startServer(mgmt, context, summary, disableHighAvailability);
+        Server server = startServer(mgmt, context, summary, disableHighAvailability);
+        if (shutdownListener!=null) {
+            // not available in some modes, eg webapp
+            shutdownListener.setServer(server);
+        }
+        return server;
     }
 
     private ContextHandler filterContextHandler(ManagementContext mgmt) {
@@ -241,7 +246,7 @@
         ResourceConfig config = new DefaultResourceConfig();
         for (Object r: BrooklynRestApi.getAllResources())
             config.getSingletons().add(r);
-        config.getSingletons().add(new ShutdownHandlerProvider(shutdownListener));
+        addShutdownListener(config, mgmt);
 
         ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
         context.setAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT, managementContext);
@@ -253,6 +258,7 @@
         return context;
     }
 
+    /** NB: not fully supported; use one of the other {@link StartMode}s */
     private ContextHandler webXmlContextHandler(ManagementContext mgmt) {
         // TODO add security to web.xml
         WebAppContext context;
@@ -266,12 +272,15 @@
             throw new IllegalStateException("Cannot find WAR for REST API. Expected in target/*.war, Maven repo, or in source directories.");
         }
         context.setAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT, mgmt);
-
+        // TODO shutdown hook
+        
         return context;
     }
 
     /** starts a server, on all NICs if security is configured,
-     * otherwise (no security) only on loopback interface */
+     * otherwise (no security) only on loopback interface 
+     * @deprecated since 0.9.0 becoming private */
+    @Deprecated
     public static Server startServer(ManagementContext mgmt, ContextHandler context, String summary, boolean disableHighAvailability) {
         // TODO this repeats code in BrooklynLauncher / WebServer. should merge the two paths.
         boolean secure = mgmt != null && !BrooklynWebConfig.hasNoSecurityOptions(mgmt.getConfig());
@@ -292,8 +301,11 @@
         return startServer(context, summary, bindLocation);
     }
 
+    /** @deprecated since 0.9.0 becoming private */
+    @Deprecated
     public static Server startServer(ContextHandler context, String summary, InetSocketAddress bindLocation) {
         Server server = new Server(bindLocation);
+        
         server.setHandler(context);
         try {
             server.start();
@@ -361,6 +373,12 @@
 
         ManagementContext mgmt = OsgiCompat.getManagementContext(context);
         config.getSingletons().add(new ManagementContextProvider(mgmt));
+        addShutdownListener(config, mgmt);
+    }
+
+    protected synchronized void addShutdownListener(ResourceConfig config, ManagementContext mgmt) {
+        if (shutdownListener!=null) throw new IllegalStateException("Can only retrieve one shutdown listener");
+        shutdownListener = new ServerStoppingShutdownHandler(mgmt);
         config.getSingletons().add(new ShutdownHandlerProvider(shutdownListener));
     }
 
diff --git a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
index dea8bca..e63a239 100644
--- a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
+++ b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
@@ -47,7 +47,7 @@
 import org.apache.brooklyn.rest.util.NullHttpServletRequestProvider;
 import org.apache.brooklyn.rest.util.NullServletConfigProvider;
 import org.apache.brooklyn.rest.util.ShutdownHandlerProvider;
-import org.apache.brooklyn.rest.util.TestShutdownHandler;
+import org.apache.brooklyn.rest.util.NoOpRecordingShutdownHandler;
 import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 
@@ -56,7 +56,7 @@
     protected ManagementContext manager;
     
     protected boolean useLocalScannedCatalog = false;
-    protected TestShutdownHandler shutdownListener = createShutdownHandler();
+    protected NoOpRecordingShutdownHandler shutdownListener = createShutdownHandler();
 
     @BeforeMethod(alwaysRun = true)
     public void setUpMethod() {
@@ -69,8 +69,8 @@
         useLocalScannedCatalog = true;
     }
     
-    private TestShutdownHandler createShutdownHandler() {
-        return new TestShutdownHandler();
+    private NoOpRecordingShutdownHandler createShutdownHandler() {
+        return new NoOpRecordingShutdownHandler();
     }
 
     protected synchronized ManagementContext getManagementContext() {
diff --git a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/NoOpRecordingShutdownHandler.java
similarity index 94%
rename from brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java
rename to brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/NoOpRecordingShutdownHandler.java
index 87fbb24..a99d3d9 100644
--- a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java
+++ b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/NoOpRecordingShutdownHandler.java
@@ -20,7 +20,7 @@
 
 import org.apache.brooklyn.rest.util.ShutdownHandler;
 
-public class TestShutdownHandler implements ShutdownHandler {
+public class NoOpRecordingShutdownHandler implements ShutdownHandler {
     private volatile boolean isRequested;
 
     @Override
diff --git a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/ServerStoppingShutdownHandler.java b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/ServerStoppingShutdownHandler.java
new file mode 100644
index 0000000..fc3bbc4
--- /dev/null
+++ b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/ServerStoppingShutdownHandler.java
@@ -0,0 +1,75 @@
+/*
+ * 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.rest.util;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
+import org.eclipse.jetty.server.Server;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/* not the cleanest way to enforce a clean shutdown, but a simple and effective way;
+ * usage is restricted to BrooklynRestApiLauncher and subclasses, to stop it inline.
+ * the main app stops the server in a more principled way using callbacks. */
+public class ServerStoppingShutdownHandler implements ShutdownHandler {
+
+    private static final Logger log = LoggerFactory.getLogger(ServerStoppingShutdownHandler.class);
+    
+    private final ManagementContext mgmt;
+    private Server server;
+    
+    public ServerStoppingShutdownHandler(ManagementContext mgmt) {
+        this.mgmt = mgmt;
+    }
+
+    @Override
+    public void onShutdownRequest() {
+        log.info("Shutting down server (when running in rest-api dev mode, using background thread)");
+
+        // essentially same as BrooklynLauncher.terminate() but cut down ...
+        // NB: this is only used in dev mode use of BrooklynJavascriptGuiLauncher
+        new Thread(new Runnable() {
+            public void run() {
+                Time.sleep(Duration.millis(250));
+                log.debug("Shutting down server in background thread, closing "+server+" and "+mgmt);
+                if (server!=null) {
+                    try {
+                        server.stop();
+                        server.join();
+                    } catch (Exception e) {
+                        log.debug("Stopping server gave an error (not usually a concern): "+e);
+                        /* NPE may be thrown e.g. if threadpool not started */
+                    }
+                }
+
+                if (mgmt instanceof ManagementContextInternal) {
+                    ((ManagementContextInternal)mgmt).terminate();
+                }
+            }
+        }).start();
+    }
+
+    /** Expect this to be injected; typically it is not known when this is created, but we need it to trigger shutdown. */
+    public void setServer(Server server) {
+        this.server = server;
+    }
+
+}
diff --git a/brooklyn-server/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java b/brooklyn-server/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java
index 1c66a04..03be76e 100644
--- a/brooklyn-server/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java
+++ b/brooklyn-server/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java
@@ -19,6 +19,12 @@
 package org.apache.brooklyn.cli;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyShell;
+import io.airlift.command.Cli;
+import io.airlift.command.Cli.CliBuilder;
+import io.airlift.command.Command;
+import io.airlift.command.Option;
 
 import java.io.Console;
 import java.io.IOException;
@@ -32,17 +38,6 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.Beta;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import com.google.common.base.Objects.ToStringHelper;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-
 import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
@@ -89,16 +84,19 @@
 import org.apache.brooklyn.util.javalang.Enums;
 import org.apache.brooklyn.util.net.Networking;
 import org.apache.brooklyn.util.text.Identifiers;
-import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
+import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import groovy.lang.GroovyClassLoader;
-import groovy.lang.GroovyShell;
-import io.airlift.command.Cli;
-import io.airlift.command.Cli.CliBuilder;
-import io.airlift.command.Command;
-import io.airlift.command.Option;
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 /**
  * This class is the primary CLI for brooklyn.
@@ -474,17 +472,23 @@
             }
             
             BrooklynServerDetails server = launcher.getServerDetails();
-            ManagementContext ctx = server.getManagementContext();
+            ManagementContext mgmt = server.getManagementContext();
             
             if (verbose) {
                 Entities.dumpInfo(launcher.getApplications());
             }
             
             if (!exitAndLeaveAppsRunningAfterStarting) {
-                waitAfterLaunch(ctx, shutdownHandler);
+                waitAfterLaunch(mgmt, shutdownHandler);
             }
 
-            // will call mgmt.terminate() in BrooklynShutdownHookJob
+            // do not shutdown servers here here -- 
+            // the BrooklynShutdownHookJob will invoke that and others on System.exit()
+            // which happens immediately after.
+            // might be nice to do it explicitly here, 
+            // but the server shutdown process has some special "shutdown apps" options
+            // so we'd want to refactor BrooklynShutdownHookJob to share code
+            
             return null;
         }
 
diff --git a/brooklyn-server/server-cli/src/main/resources/brooklyn/default.catalog.bom b/brooklyn-server/server-cli/src/main/resources/brooklyn/default.catalog.bom
index 483f04d..97dc8a7 100644
--- a/brooklyn-server/server-cli/src/main/resources/brooklyn/default.catalog.bom
+++ b/brooklyn-server/server-cli/src/main/resources/brooklyn/default.catalog.bom
@@ -181,6 +181,8 @@
         brooklyn.config:
           my.message:   $brooklyn:formatString("connected to Riak at %s",
                             $brooklyn:entity("riak-cluster").attributeWhenReady("main.uri"))
+        # and clear the location defined there so it is taken from this template
+        locations:      []
                             
       # use the off-the-shelf Riak cluster
       - type:           org.apache.brooklyn.entity.nosql.riak.RiakCluster
@@ -240,6 +242,8 @@
               # and a lot of sensors defined
               type:           2-bash-web-server-template
               name:           My Bash Web Server VM with Sensors
+              # and clear the location defined there so it is taken from this template
+              locations:      []
               
               brooklyn.config:
                 my.message:   "part of the cluster"
@@ -332,6 +336,8 @@
           # point this load balancer at the cluster, specifying port to forward to
           loadbalancer.serverpool:  $brooklyn:entity("my-web-cluster")
           member.sensor.portNumber: app.port
+          # disable sticky sessions to allow easy validation of balancing via browser refresh
+          nginx.sticky: false
       
       brooklyn.enrichers:
       # publish a few useful info sensors and KPI's to the root of the app
diff --git a/brooklyn-server/software/base/pom.xml b/brooklyn-server/software/base/pom.xml
index 556e982..51ae966 100644
--- a/brooklyn-server/software/base/pom.xml
+++ b/brooklyn-server/software/base/pom.xml
@@ -163,6 +163,10 @@
                 <configuration>
                     <instructions>
                         <Include-Resource>{maven-resources}, target/classes/brooklyn-jmxmp-agent-shaded-${project.version}.jar, target/classes/brooklyn-jmxrmi-agent-${project.version}.jar</Include-Resource>
+                        <Import-Package>
+                            !org.python.core,
+                            *
+                        </Import-Package>
                     </instructions>
                 </configuration>
             </plugin>
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 59dac4f..d243833 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,23 +107,22 @@
             skipStart = entityStarted.or(false);
         }
         if (!skipStart) {
-            DynamicTasks.queue("copy-pre-install-resources", new Runnable() { public void run() {
-                waitForConfigKey(BrooklynConfigKeys.PRE_INSTALL_RESOURCES_LATCH);
-                copyPreInstallResources();
-            }});
-
-            DynamicTasks.queue("pre-install", new Runnable() { public void run() {
-                preInstall();
-            }});
-
-            DynamicTasks.queue("pre-install-command", new Runnable() { public void run() {
-                runPreInstallCommand();
-            }});
-
             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", new Runnable() { public void run() {
+                    preInstall();
+                }});
+
+                DynamicTasks.queue("pre-install-command", new Runnable() { public void run() {
+                    runPreInstallCommand();
+                }});
                 DynamicTasks.queue("setup", new Runnable() { public void run() {
                     waitForConfigKey(BrooklynConfigKeys.SETUP_LATCH);
                     setup();
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
index 6a39cc2..de88cba 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
@@ -48,7 +48,6 @@
 import org.apache.brooklyn.util.stream.Streams;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Duration;
-import org.python.core.PyException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -239,7 +238,7 @@
         if (Strings.isBlank(regularCommand)) {
             response = getLocation().executePsScript(ImmutableList.of(powerShellCommand));
         } else {
-            response = getLocation().executeScript(ImmutableList.of(regularCommand));
+            response = getLocation().executeCommand(ImmutableList.of(regularCommand));
         }
 
         if (currentTask != null) {
@@ -259,7 +258,7 @@
     }
 
     public int execute(List<String> script) {
-        return getLocation().executeScript(script).getStatusCode();
+        return getLocation().executeCommand(script).getStatusCode();
     }
 
     public int executePsScriptNoRetry(List<String> psScript) {
@@ -281,8 +280,9 @@
     public void rebootAndWait() {
         try {
             executePsScriptNoRetry(ImmutableList.of("Restart-Computer -Force"));
-        } catch (PyException e) {
+        } catch (Exception e) {
             // Restarting the computer will cause the command to fail; ignore the exception and continue
+            Exceptions.propagateIfFatal(e);
         }
         waitForWinRmStatus(false, entity.getConfig(VanillaWindowsProcess.REBOOT_BEGUN_TIMEOUT));
         waitForWinRmStatus(true, entity.getConfig(VanillaWindowsProcess.REBOOT_COMPLETED_TIMEOUT)).getWithError();
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcess.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcess.java
index 98a97c0..3fd694c 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcess.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcess.java
@@ -19,10 +19,14 @@
 package org.apache.brooklyn.entity.software.base;
 
 import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
 
 @ImplementedBy(EmptySoftwareProcessImpl.class)
 public interface EmptySoftwareProcess extends SoftwareProcess {
 
+    ConfigKey<Boolean> USE_SSH_MONITORING = ConfigKeys.newConfigKey("sshMonitoring.enabled", "SSH monitoring enabled", Boolean.TRUE);
+
     public SoftwareProcessDriver getDriver();
     
 }
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcessImpl.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcessImpl.java
index 154a08a..7330461 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcessImpl.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcessImpl.java
@@ -18,6 +18,8 @@
  */
 package org.apache.brooklyn.entity.software.base;
 
+import org.apache.brooklyn.core.entity.Attributes;
+
 public class EmptySoftwareProcessImpl extends SoftwareProcessImpl implements EmptySoftwareProcess {
 
     @Override
@@ -28,7 +30,11 @@
     @Override
     protected void connectSensors() {
         super.connectSensors();
-        connectServiceUpIsRunning();
+        if (isSshMonitoringEnabled()) {
+            connectServiceUpIsRunning();
+        } else {
+            sensors().set(Attributes.SERVICE_UP, true);
+        }
     }
 
     @Override
@@ -36,4 +42,8 @@
         disconnectServiceUpIsRunning();
         super.disconnectSensors();
     }
+    
+    protected boolean isSshMonitoringEnabled() {
+        return Boolean.TRUE.equals(getConfig(USE_SSH_MONITORING));
+    }
 }
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcess.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcess.java
new file mode 100644
index 0000000..770ba0d
--- /dev/null
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcess.java
@@ -0,0 +1,38 @@
+/*
+ * 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.entity.software.base;
+
+import java.util.Collection;
+
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+
+import com.google.common.collect.ImmutableSet;
+
+@ImplementedBy(EmptyWindowsProcessImpl.class)
+public interface EmptyWindowsProcess extends SoftwareProcess {
+
+    // 3389 is RDP; 5985 is WinRM (3389 isn't used by Brooklyn, but useful for the end-user subsequently)
+    ConfigKey<Collection<Integer>> REQUIRED_OPEN_LOGIN_PORTS = ConfigKeys.newConfigKeyWithDefault(
+            SoftwareProcess.REQUIRED_OPEN_LOGIN_PORTS,
+            ImmutableSet.of(5985, 3389));
+    
+    ConfigKey<Boolean> USE_WINRM_MONITORING = ConfigKeys.newConfigKey("winrmMonitoring.enabled", "WinRM monitoring enabled", Boolean.TRUE);
+}
diff --git a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessDriver.java
similarity index 65%
copy from brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java
copy to brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessDriver.java
index 87fbb24..d6e1c99 100644
--- a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessDriver.java
@@ -16,24 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.brooklyn.rest.util;
+package org.apache.brooklyn.entity.software.base;
 
-import org.apache.brooklyn.rest.util.ShutdownHandler;
-
-public class TestShutdownHandler implements ShutdownHandler {
-    private volatile boolean isRequested;
-
-    @Override
-    public void onShutdownRequest() {
-        isRequested = true;
-    }
-
-    public boolean isRequested() {
-        return isRequested;
-    }
-
-    public void reset() {
-        isRequested = false;
-    }
-
+public interface EmptyWindowsProcessDriver extends SoftwareProcessDriver {
 }
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessImpl.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessImpl.java
new file mode 100644
index 0000000..ae87774
--- /dev/null
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessImpl.java
@@ -0,0 +1,49 @@
+/*
+ * 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.entity.software.base;
+
+import org.apache.brooklyn.core.entity.Attributes;
+
+public class EmptyWindowsProcessImpl extends SoftwareProcessImpl implements EmptyWindowsProcess {
+
+    @Override
+    public Class<?> getDriverInterface() {
+        return EmptyWindowsProcessDriver.class;
+    }
+
+    @Override
+    protected void connectSensors() {
+        super.connectSensors();
+        if (isWinrmMonitoringEnabled()) {
+            connectServiceUpIsRunning();
+        } else {
+            sensors().set(Attributes.SERVICE_UP, true);
+        }
+    }
+
+    @Override
+    protected void disconnectSensors() {
+        disconnectServiceUpIsRunning();
+        super.disconnectSensors();
+    }
+    
+    protected boolean isWinrmMonitoringEnabled() {
+        return Boolean.TRUE.equals(getConfig(USE_WINRM_MONITORING));
+    }
+}
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessWinRmDriver.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessWinRmDriver.java
new file mode 100644
index 0000000..4a9a054
--- /dev/null
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessWinRmDriver.java
@@ -0,0 +1,97 @@
+/*
+ * 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.entity.software.base;
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.apache.brooklyn.util.net.UserAndHostAndPort;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EmptyWindowsProcessWinRmDriver extends AbstractSoftwareProcessWinRmDriver implements VanillaWindowsProcessDriver {
+    @SuppressWarnings("unused")
+    private static final Logger LOG = LoggerFactory.getLogger(EmptyWindowsProcessWinRmDriver.class);
+
+    private final AtomicBoolean running = new AtomicBoolean();
+
+    public EmptyWindowsProcessWinRmDriver(EmptyWindowsProcessImpl entity, WinRmMachineLocation machine) {
+        super(entity, machine);
+    }
+
+    @Override
+    public void start() {
+        WinRmMachineLocation machine = (WinRmMachineLocation) location;
+        UserAndHostAndPort winrmAddress = UserAndHostAndPort.fromParts(machine.getUser(), machine.getAddress().getHostName(), machine.config().get(WinRmMachineLocation.WINRM_PORT));
+        getEntity().sensors().set(Attributes.WINRM_ADDRESS, winrmAddress);
+
+        super.start();
+    }
+
+    @Override
+    public boolean isRunning() {
+        return running.get();
+    }
+
+    @Override
+    public void install() { }
+
+    @Override
+    public void customize() { }
+
+    @Override
+    public void copyInstallResources() { 
+        Map<String, String> installFiles = entity.getConfig(SoftwareProcess.INSTALL_FILES);
+        Map<String, String> installTemplates = entity.getConfig(SoftwareProcess.INSTALL_TEMPLATES);
+        if ((installFiles!=null && !installFiles.isEmpty()) || (installTemplates!=null && !installTemplates.isEmpty())) {
+            // only do this if there are files, to prevent unnecessary `mkdir`
+            super.copyInstallResources();
+        }
+    }
+
+    @Override
+    public void copyRuntimeResources() { 
+        Map<String, String> runtimeFiles = entity.getConfig(SoftwareProcess.RUNTIME_FILES);
+        Map<String, String> runtimeTemplates = entity.getConfig(SoftwareProcess.RUNTIME_TEMPLATES);
+        if ((runtimeFiles!=null && !runtimeFiles.isEmpty()) || (runtimeTemplates!=null && !runtimeTemplates.isEmpty())) {
+            // only do this if there are files, to prevent unnecessary `mkdir`
+            super.copyRuntimeResources();
+        }        
+    }
+
+    @Override
+    public void launch() {
+        running.set(true);
+    }
+
+    @Override
+    public void rebind() {
+        super.rebind();
+        /* TODO not necessarily, but there is not yet an easy way to persist state without 
+         * using config/sensors which we might not want do. */
+        running.set(true);
+    }
+
+    @Override
+    public void stop() {
+        running.set(false);
+    }
+}
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java
index c24201a..8da252f 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java
@@ -21,6 +21,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.util.Collection;
+import java.util.List;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.mgmt.Task;
@@ -28,6 +29,7 @@
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers;
+import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
 import org.apache.brooklyn.util.collections.QuorumCheck;
 import org.apache.brooklyn.util.core.config.ConfigBag;
@@ -68,7 +70,10 @@
      * Subclasses should override {@link #doStart} to customise behaviour.
      */
     @Override
-    public final void start(final Collection<? extends Location> locations) {
+    public final void start(Collection<? extends Location> locsO) {
+        addLocations(locsO);
+        final Collection<? extends Location> locations = Locations.getLocationsCheckingAncestors(locsO, this);
+        
         checkNotNull(locations, "locations");
         if (DynamicTasks.getTaskQueuingContext() != null) {
             doStart(locations);
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
new file mode 100644
index 0000000..77175d2
--- /dev/null
+++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java
@@ -0,0 +1,211 @@
+/*
+ * 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.entity.software.base.test.autoscaling;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
+import org.apache.brooklyn.core.entity.trait.Resizable;
+import org.apache.brooklyn.core.mgmt.internal.CollectionChangeListener;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestCluster;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+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.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+
+public class AutoScalerPolicyNoMoreMachinesTest extends BrooklynAppUnitTestSupport {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(AutoScalerPolicyNoMoreMachinesTest.class);
+    
+    private static long SHORT_WAIT_MS = 250;
+    
+    DynamicCluster cluster;
+    Location loc;
+    AutoScalerPolicy policy;
+    Set<Entity> entitiesAdded;
+    Set<Entity> entitiesRemoved;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        super.setUp();
+        cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
+                .configure(TestCluster.INITIAL_SIZE, 0)
+                .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(EmptySoftwareProcess.class)
+                        .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true)));
+        loc = mgmt.getLocationRegistry().resolve("byon(hosts='1.1.1.1,1.1.1.2')");
+        app.start(ImmutableList.of(loc));
+        
+        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);
+            }});
+    }
+
+    @Test
+    public void testResizeDirectly() throws Exception {
+        assertSize(0);
+
+        cluster.resize(2);
+        assertSize(2);
+        
+        // Won't get a location to successfully resize (byon location only has 2 machines); 
+        // so still left with 2 members (failed node not quarantined, because exception well understood)
+        try {
+            cluster.resize(3);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            Asserts.expectedFailureOfType(e, Resizable.InsufficientCapacityException.class);
+        }
+        assertSize(2, 0, 1);
+
+        // Resize down; will delete one of our nodes
+        cluster.resize(1);
+        assertSize(1, 0, 2);
+        
+        // Resize back up to 2 should be allowed
+        cluster.resize(2);
+        assertSize(2, 0, 2);
+    }
+    
+    @Test
+    public void testPoolHotSensorResizingBeyondMaxMachines() throws Exception {
+        cluster.resize(1);
+        policy = cluster.policies().add(PolicySpec.create(AutoScalerPolicy.class)
+                .configure(AutoScalerPolicy.MIN_PERIOD_BETWEEN_EXECS, Duration.millis(10)));
+
+        // Single node trying to handle a load of 21; too high, so will add one more node
+        cluster.sensors().emit(AutoScalerPolicy.DEFAULT_POOL_HOT_SENSOR, message(21L, 10L, 20L));
+        assertSizeEventually(2);
+
+        // Two nodes handing an aggregated load of 41; too high for 2 nodes so tries to scale to 3.
+        // But byon location only has 2 nodes so will fail.
+        cluster.sensors().emit(AutoScalerPolicy.DEFAULT_POOL_HOT_SENSOR, message(21L, 10L, 20L));
+        assertSizeEventually(2, 0, 1);
+
+        // Should not repeatedly retry
+        assertSizeContinually(2, 0, 1);
+        
+        // If there is another indication of too much load, should not retry yet again.
+        cluster.sensors().emit(AutoScalerPolicy.DEFAULT_POOL_HOT_SENSOR, message(42L, 10L, 20L));
+        assertSizeContinually(2, 0, 1);
+    }
+    
+    @Test
+    public void testMetricResizingBeyondMaxMachines() throws Exception {
+        AttributeSensor<Integer> metric = Sensors.newIntegerSensor("test.aggregatedLoad");
+        
+        cluster.resize(1);
+        policy = cluster.policies().add(PolicySpec.create(AutoScalerPolicy.class)
+                .configure(AutoScalerPolicy.METRIC, metric)
+                .configure(AutoScalerPolicy.METRIC_LOWER_BOUND, 10)
+                .configure(AutoScalerPolicy.METRIC_UPPER_BOUND, 20)
+                .configure(AutoScalerPolicy.MIN_PERIOD_BETWEEN_EXECS, Duration.millis(10)));
+
+        // Single node trying to handle a load of 21; too high, so will add one more node.
+        // That takes the load back to within acceptable limits
+        cluster.sensors().set(metric, 21);
+        assertSizeEventually(2);
+        cluster.sensors().set(metric, 19);
+
+        // With two nodes, load is now too high, so will try (and fail) to add one more node.
+        // Trigger another attempt to resize.
+        // Any nodes that fail with NoMachinesAvailableException will be immediately deleted.
+        cluster.sensors().set(metric, 22);
+        assertSizeEventually(2, 0, 1);
+        assertSizeContinually(2, 0, 1);
+        
+        // Metric is re-published; should not keep retrying
+        cluster.sensors().set(metric, 21);
+        assertSizeContinually(2, 0, 1);
+    }
+
+    protected Map<String, Object> message(double currentWorkrate, double lowThreshold, double highThreshold) {
+        return message(cluster.getCurrentSize(), currentWorkrate, lowThreshold, highThreshold);
+    }
+    
+    protected Map<String, Object> message(int currentSize, double currentWorkrate, double lowThreshold, double highThreshold) {
+        return ImmutableMap.<String,Object>of(
+            AutoScalerPolicy.POOL_CURRENT_SIZE_KEY, currentSize,
+            AutoScalerPolicy.POOL_CURRENT_WORKRATE_KEY, currentWorkrate,
+            AutoScalerPolicy.POOL_LOW_THRESHOLD_KEY, lowThreshold,
+            AutoScalerPolicy.POOL_HIGH_THRESHOLD_KEY, highThreshold);
+    }
+    
+    protected void assertSize(Integer targetSize) {
+        assertSize(targetSize, 0);
+    }
+
+    protected void assertSize(int targetSize, int quarantineSize, final int deletedSize) {
+        assertSize(targetSize, quarantineSize);
+        assertEquals(entitiesRemoved.size(), deletedSize, "removed="+entitiesRemoved);
+    }
+    
+    protected void assertSize(int targetSize, int quarantineSize) {
+        assertEquals(cluster.getCurrentSize(), (Integer) targetSize, "cluster.currentSize");
+        assertEquals(cluster.getMembers().size(), targetSize, "cluster.members.size");
+        assertEquals(cluster.sensors().get(DynamicCluster.QUARANTINE_GROUP).getMembers().size(), quarantineSize, "cluster.quarantine.size");
+        assertEquals(mgmt.getEntityManager().findEntities(Predicates.instanceOf(EmptySoftwareProcess.class)).size(), targetSize + quarantineSize, "instanceCount(EmptySoftwareProcess)");
+    }
+    
+    protected void assertSizeEventually(int targetSize) {
+        assertSizeEventually(targetSize, 0, 0);
+    }
+    
+    protected void assertSizeEventually(final int targetSize, final int quarantineSize, final int deletedSize) {
+        Asserts.succeedsEventually(new Runnable() {
+            public void run() {
+                assertSize(targetSize, quarantineSize);
+                assertEquals(entitiesRemoved.size(), deletedSize, "removed="+entitiesRemoved);
+            }});
+    }
+    
+    protected void assertSizeContinually(final int targetSize, final int quarantineSize, final int deletedSize) {
+        Asserts.succeedsContinually(ImmutableMap.of("timeout", SHORT_WAIT_MS), new Runnable() {
+            public void run() {
+                assertSize(targetSize, quarantineSize);
+                assertEquals(entitiesRemoved.size(), deletedSize, "removed="+entitiesRemoved);
+            }});
+    }
+}
diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
index 884da19..2e34ef7 100644
--- a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
+++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
@@ -63,7 +63,7 @@
  * Tests execution of commands (batch and powershell) on Windows over WinRM, and of
  * file upload.
  * 
- * There are limitations with what is supported by PyWinRM. These are highlighted in
+ * There are limitations with what is supported by winrm4j. These are highlighted in
  * tests marked as "WIP" (see individual tests).
  * 
  * These limitations are documented in docs/guide/yaml/winrm/index.md.
@@ -146,7 +146,7 @@
         String remotePath = "C:\\myfile-"+Identifiers.makeRandomId(4)+".txt";
         machine.copyTo(new ByteArrayInputStream(contents.getBytes()), remotePath);
         
-        WinRmToolResponse response = machine.executeScript("type "+remotePath);
+        WinRmToolResponse response = machine.executeCommand("type "+remotePath);
         String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr();
         assertEquals(response.getStatusCode(), 0, msg);
         assertEquals(response.getStdOut().trim(), contents, msg);
@@ -159,7 +159,7 @@
             String remotePath = "C:\\myfile-"+Identifiers.makeRandomId(4)+".txt";
             machine.copyTo(localFile, remotePath);
             
-            WinRmToolResponse response = machine.executeScript("type "+remotePath);
+            WinRmToolResponse response = machine.executeCommand("type "+remotePath);
             String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr();
             assertEquals(response.getStatusCode(), 0, msg);
             assertEquals(response.getStdOut().trim(), contents, msg);
@@ -174,26 +174,18 @@
     }
     
     /*
-     * TODO Not supported in PyWinRM.
+     * TODO Not supported in winrm4j (or PyWinRM).
      * 
-     * Executing (in python):
-     *     import winrm
-     *     s = winrm.Session('52.12.211.247', auth=('Administrator', 'pa55w0rd'))
-     *     r = s.run_cmd("echo first \r\n echo second")
-     * gives just "first".
+     * Just gives "first", and exit code 1.
      */
-    @Test(groups={"Live", "WIP"})
+    @Test(groups={"Live", "WIP"}, enabled=false)
     public void testExecMultiLineScript() throws Exception {
         assertExecSucceeds("echo first" + "\r\n" + "echo second", "first"+"\r\n"+"second", "");
     }
     
-    /*
-     * TODO Not supported in PyWinRM. Under the covers, we just concatenate the commands.
-     * See {@link #testExecMultiLineScript()}.
-     */
-    @Test(groups={"Live", "WIP"})
+    @Test(groups={"Live"})
     public void testExecMultiPartScript() throws Exception {
-        assertExecSucceeds(ImmutableList.of("echo first", "echo second"), "first"+"\r\n"+"second", "");
+        assertExecSucceeds(ImmutableList.of("echo first", "echo second"), "first "+"\r\n"+"second", "");
     }
     
     @Test(groups="Live")
@@ -212,7 +204,7 @@
     }
 
     /*
-     * TODO Not supported in PyWinRM.
+     * TODO Not supported in winrm4j (or PyWinRM).
      * 
      * Executing (in python):
      *     import winrm
@@ -443,7 +435,7 @@
     }
 
     /*
-     * TODO Not supported in PyWinRM - single line .ps1 file with "exit 1" gives an
+     * TODO Not supported in winrm4j - single line .ps1 file with "exit 1" gives an
      * exit code 0 over PyWinRM, but an exit code 1 when executed locally!
      * 
      * Executing (in python):
@@ -554,12 +546,12 @@
 
     private void assertExecFails(String cmd) {
         Stopwatch stopwatch = Stopwatch.createStarted();
-        assertFailed(cmd, machine.executeScript(cmd), stopwatch);
+        assertFailed(cmd, machine.executeCommand(cmd), stopwatch);
     }
 
     private void assertExecFails(List<String> cmds) {
         Stopwatch stopwatch = Stopwatch.createStarted();
-        assertFailed(cmds, machine.executeScript(cmds), stopwatch);
+        assertFailed(cmds, machine.executeCommand(cmds), stopwatch);
     }
     
     private void assertExecPsFails(String cmd) {
@@ -574,12 +566,12 @@
 
     private void assertExecSucceeds(String cmd, String stdout, String stderr) {
         Stopwatch stopwatch = Stopwatch.createStarted();
-        assertSucceeded(cmd, machine.executeScript(cmd), stdout, stderr, stopwatch);
+        assertSucceeded(cmd, machine.executeCommand(cmd), stdout, stderr, stopwatch);
     }
 
     private void assertExecSucceeds(List<String> cmds, String stdout, String stderr) {
         Stopwatch stopwatch = Stopwatch.createStarted();
-        assertSucceeded(cmds, machine.executeScript(cmds), stdout, stderr, stopwatch);
+        assertSucceeded(cmds, machine.executeCommand(cmds), stdout, stderr, stopwatch);
     }
 
     private void assertExecPsSucceeds(String cmd, String stdout, String stderr) {
diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java
index b6f8684..1939ed0 100644
--- a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java
+++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java
@@ -116,8 +116,8 @@
         private String uniqueTag;
         private volatile boolean built;
 
-        public Builder entity(EntityLocal val) {
-            this.entity = checkNotNull(val, "entity");
+        public Builder entity(Entity val) {
+            this.entity = (EntityLocal) checkNotNull(val, "entity");
             return this;
         }
         public Builder addSensor(WindowsPerformanceCounterPollConfig<?> config) {
diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
index 8a2a786..d04c366 100644
--- a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
+++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
@@ -44,7 +44,7 @@
 import org.apache.brooklyn.util.core.internal.ssh.SshTool;
 import org.apache.brooklyn.util.core.internal.winrm.WinRmTool;
 import org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse;
-import org.apache.brooklyn.util.core.internal.winrm.pywinrm.Winrm4jTool;
+import org.apache.brooklyn.util.core.internal.winrm.winrm4j.Winrm4jTool;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.stream.Streams;
 import org.apache.brooklyn.util.text.Strings;
@@ -212,17 +212,50 @@
         return (result == null) ? ImmutableSet.<String>of() : ImmutableSet.copyOf(result);
     }
 
+    /**
+     * @deprecated since 0.9.0; use {@link #executeCommand(String)}
+     */
+    @Deprecated
     public WinRmToolResponse executeScript(String script) {
-        return executeScript(ImmutableList.of(script));
+        return executeCommand(script);
     }
 
+    /**
+     * @deprecated since 0.9.0; use {@link #executeCommand(List)}
+     */
+    @Deprecated
     public WinRmToolResponse executeScript(List<String> script) {
-        return executeScript(ImmutableMap.of(), script);
+        return executeCommand(script);
     }
     
+    /**
+     * @deprecated since 0.9.0; use {@link #executeCommand(Map, List)}
+     */
+    @Deprecated
     public WinRmToolResponse executeScript(Map<?,?> props, List<String> script) {
+        return executeCommand(props, script);
+    }
+
+    /**
+     * @since 0.9.0 (previously was {@code executeScript(String)}
+     */
+    public WinRmToolResponse executeCommand(String script) {
+        return executeCommand(ImmutableList.of(script));
+    }
+
+    /**
+     * @since 0.9.0 (previously was {@code executeScript(List)}
+     */
+    public WinRmToolResponse executeCommand(List<String> script) {
+        return executeCommand(ImmutableMap.of(), script);
+    }
+    
+    /**
+     * @since 0.9.0 (previously was {@code executeScript(Map, List)}
+     */
+    public WinRmToolResponse executeCommand(Map<?,?> props, List<String> script) {
         WinRmTool tool = newWinRmTool(props);
-        return tool.executeScript(script);
+        return tool.executeCommand(script);
     }
 
     public WinRmToolResponse executePsScript(String psScript) {
diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java
index 6d2fb06..5515ec5 100644
--- a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java
+++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java
@@ -66,8 +66,17 @@
             "Size of file chunks (in bytes) to be used when copying a file to the remote server", 
             1024);
 
+    /**
+     * @deprecated since 0.9.0; use {@link #executeCommand(List)} to avoid ambiguity between native command and power shell.
+     */
+    @Deprecated
     WinRmToolResponse executeScript(List<String> commands);
 
+    /**
+     * @since 0.9.0
+     */
+    WinRmToolResponse executeCommand(List<String> commands);
+
     WinRmToolResponse executePs(List<String> commands);
     
     WinRmToolResponse copyToServer(InputStream source, String destination);
diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java
similarity index 96%
rename from brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java
rename to brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java
index 0bdfa14..e023cfb 100644
--- a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java
+++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.brooklyn.util.core.internal.winrm.pywinrm;
+package org.apache.brooklyn.util.core.internal.winrm.winrm4j;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -84,15 +84,21 @@
     }
     
     @Override
-    public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executeScript(final List<String> commands) {
+    public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executeCommand(final List<String> commands) {
         return exec(new Function<io.cloudsoft.winrm4j.winrm.WinRmTool, io.cloudsoft.winrm4j.winrm.WinRmToolResponse>() {
             @Override public WinRmToolResponse apply(io.cloudsoft.winrm4j.winrm.WinRmTool tool) {
-                return tool.executeScript(commands);
+                return tool.executeCommand(commands);
             }
         });
     }
 
     @Override
+    @Deprecated
+    public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executeScript(final List<String> commands) {
+        return executeCommand(commands);
+    }
+    
+    @Override
     public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executePs(final List<String> commands) {
         return exec(new Function<io.cloudsoft.winrm4j.winrm.WinRmTool, io.cloudsoft.winrm4j.winrm.WinRmToolResponse>() {
             @Override public WinRmToolResponse apply(io.cloudsoft.winrm4j.winrm.WinRmTool tool) {
diff --git a/brooklyn-server/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedLiveTest.java b/brooklyn-server/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedLiveTest.java
index de7d481..412a732 100644
--- a/brooklyn-server/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedLiveTest.java
+++ b/brooklyn-server/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedLiveTest.java
@@ -20,16 +20,15 @@
 
 import java.util.Map;
 
-import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.entity.EntityAsserts;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
 import org.apache.brooklyn.core.test.entity.TestEntity;
-import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -51,8 +50,8 @@
  * <pre>
  * {@code brooklyn.location.named.WindowsLiveTest=byon:(hosts="ec2-xx-xxx-xxx-xx.eu-west-1.compute.amazonaws.com")
  * brooklyn.location.named.WindowsLiveTest.user=Administrator
- * brooklyn.location.named.WindowsLiveTest.privateKeyFile = ~/.ssh/id_rsa
- * brooklyn.location.named.WindowsLiveTest.publicKeyFile = ~/.ssh/id_rsa.pub
+ * brooklyn.location.named.WindowsLiveTest.password=pa55word
+ * brooklyn.location.named.WindowsLiveTest.osFamily=windows
  * }</pre>
  * The location must by {@code byon} or another primitive type. Unfortunately, it's not possible to
  * use a jclouds location, as adding a dependency on brooklyn-locations-jclouds would cause a
@@ -66,7 +65,7 @@
     private static final String LOCATION_SPEC = "named:WindowsLiveTest";
 
     private Location loc;
-    private EntityLocal entity;
+    private Entity entity;
 
     @BeforeMethod(alwaysRun=true)
     public void setUp() throws Exception {
@@ -75,16 +74,15 @@
         Map<String,?> allFlags = MutableMap.<String,Object>builder()
                 .put("tags", ImmutableList.of(getClass().getName()))
                 .build();
-        MachineProvisioningLocation<? extends MachineLocation> provisioningLocation =
-                (MachineProvisioningLocation<? extends MachineLocation>)
-                        mgmt.getLocationRegistry().resolve(LOCATION_SPEC, allFlags);
+        MachineProvisioningLocation<?> provisioningLocation = (MachineProvisioningLocation<?>) 
+                mgmt.getLocationRegistry().resolve(LOCATION_SPEC, allFlags);
         loc = provisioningLocation.obtain(ImmutableMap.of());
 
         entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
         app.start(ImmutableList.of(loc));
     }
 
-    @Test(groups={"Live","Disabled"}, enabled=false)
+    @Test(groups={"Live","Disabled"})
     public void testRetrievesPerformanceCounters() throws Exception {
         // We can be pretty sure that a Windows instance in the cloud will have zero telephone lines...
         entity.sensors().set(TELEPHONE_LINES, 42);
@@ -94,7 +92,7 @@
                 .addSensor("\\Telephony\\Lines", TELEPHONE_LINES)
                 .build();
         try {
-            EntityTestUtils.assertAttributeEqualsEventually(entity, TELEPHONE_LINES, 0);
+            EntityAsserts.assertAttributeEqualsEventually(entity, TELEPHONE_LINES, 0);
         } finally {
             feed.stop();
         }
diff --git a/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java b/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java
index 469bc3d..3ded474 100644
--- a/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java
+++ b/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java
@@ -49,7 +49,7 @@
         try {
             // Get an unsubmitted task for starting all the children of this entity in parallel,
             // at the same location as this entity.
-            final TaskAdaptable<?> taskAdaptable = StartableMethods.startingChildren(this);
+            final TaskAdaptable<?> taskAdaptable = StartableMethods.startingChildren(this, locations);
             logger.trace("{}, TaskAdaptable: {}", this, taskAdaptable);
 
             // Submit the task to the ExecutionManager so that they actually get started
diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/InfrastructureDeploymentTestCaseTest.java b/brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/InfrastructureDeploymentTestCaseTest.java
similarity index 100%
rename from usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/InfrastructureDeploymentTestCaseTest.java
rename to brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/InfrastructureDeploymentTestCaseTest.java
diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/LoopOverGroupMembersTestCaseTest.java b/brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/LoopOverGroupMembersTestCaseTest.java
similarity index 100%
rename from usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/LoopOverGroupMembersTestCaseTest.java
rename to brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/LoopOverGroupMembersTestCaseTest.java
diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructure.java b/brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructure.java
similarity index 100%
rename from usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructure.java
rename to brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructure.java
diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructureImpl.java b/brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructureImpl.java
similarity index 100%
rename from usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructureImpl.java
rename to brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructureImpl.java
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 2bd7b5c..15aa76e 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
@@ -1115,22 +1115,29 @@
         if (e instanceof ShouldHaveFailedPreviouslyAssertionError) throw (Error)e;
     }
     
-    /** Tests that an exception is not {@link ShouldHaveFailedPreviouslyAssertionError}
-     * and is one of the given types. 
+    /**
+     * Tests that an exception is not {@link ShouldHaveFailedPreviouslyAssertionError}
+     * and is either one of the given types, or has a caused-by of one of the given types.
+     * 
+     * If you want *just* the instanceof (without checking the caused-by), then just catch
+     * those exception types, treat that as success, and let any other exception be propagated.
      * 
      * @return If the test is satisfied, this method returns normally. 
      * The caller can decide whether anything more should be done with the exception.
      * If the test fails, then either it is propagated, 
      * if the {@link Throwable} is a fatal ({@link Exceptions#propagateIfFatal(Throwable)}) other than an {@link AssertionError}, 
      * or more usually the test failure of this method is thrown, 
-     * with detail of the original {@link Throwable} logged. */
+     * with detail of the original {@link Throwable} logged and included in the caused-by.
+     */
     public static void expectedFailureOfType(Throwable e, Class<?> ...permittedSupertypes) {
         if (e instanceof ShouldHaveFailedPreviouslyAssertionError) throw (Error)e;
-        for (Class<?> t: permittedSupertypes) {
-            if (t.isInstance(e)) return;
+        for (Class<?> clazz: permittedSupertypes) {
+            @SuppressWarnings("unchecked")
+            Throwable match = Exceptions.getFirstThrowableOfType(e, (Class<? extends Throwable>)clazz);
+            if (match != null) return;
         }
         rethrowPreferredException(e, 
-            new AssertionError("Error "+JavaClassNames.simpleClassName(e)+" is not any of the expected types: " + Arrays.asList(permittedSupertypes)));
+            new AssertionError("Error "+JavaClassNames.simpleClassName(e)+" is not any of the expected types: " + Arrays.asList(permittedSupertypes), e));
     }
     
     /** Tests {@link #expectedFailure(Throwable)} and that the <code>toString</code>
diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
index 9643018..314961a 100644
--- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
+++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
@@ -33,6 +33,7 @@
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
 import org.apache.brooklyn.util.text.Strings;
 
 import com.google.common.base.Predicate;
@@ -324,7 +325,23 @@
             return new CompoundRuntimeException(prefix, exceptions);
         }
         if (Strings.isBlank(prefix)) return new CompoundRuntimeException(exceptions.size()+" errors, including: " + Exceptions.collapseText(exceptions.iterator().next()), exceptions);
-        return new CompoundRuntimeException(prefix+", "+exceptions.size()+" errors including: " + Exceptions.collapseText(exceptions.iterator().next()), exceptions);
+        return new CompoundRuntimeException(prefix+"; "+exceptions.size()+" errors including: " + Exceptions.collapseText(exceptions.iterator().next()), exceptions);
+    }
+
+    /** Some throwables require a prefix for the message to make sense,
+     * for instance NoClassDefFoundError's message is often just the type.
+     */
+    public static boolean isPrefixImportant(Throwable t) {
+        if (t instanceof NoClassDefFoundError) return true;
+        return false;
+    }
+
+    /** For {@link Throwable} instances where know {@link #isPrefixImportant(Throwable)},
+     * this returns a nice message for use as a prefix; otherwise this returns the class name.
+     * Callers should typically suppress the prefix if {@link #isPrefixBoring(Throwable)} is true. */
+    public static String getPrefixText(Throwable t) {
+        if (t instanceof NoClassDefFoundError) return "Type not found";
+        return JavaClassNames.cleanSimpleClassName(t);
     }
 
 }
diff --git a/brooklyn-ui/src/main/license/files/LICENSE b/brooklyn-ui/src/main/license/files/LICENSE
index 4a1f38b..3b3b73d 100644
--- a/brooklyn-ui/src/main/license/files/LICENSE
+++ b/brooklyn-ui/src/main/license/files/LICENSE
@@ -208,13 +208,6 @@
   Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
   Copyright (c) Jeremy Ashkenas, DocumentCloud Inc. (2010-2013)
 
-This project includes the software: backbone.js
-  Available at: http://backbonejs.org
-  Developed by: DocumentCloud Inc. (http://www.documentcloud.org/)
-  Version used: 1.1.2
-  Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
-  Copyright (c) Jeremy Ashkenas, DocumentCloud (2010-2014)
-
 This project includes the software: bootstrap.js
   Available at: http://twitter.github.com/bootstrap/javascript.html#transitions
   Version used: 2.0.4
@@ -229,14 +222,6 @@
   Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
   Copyright (c) Yehuda Katz (2012)
 
-This project includes the software: handlebars.js
-  Available at: https://github.com/wycats/handlebars.js
-  Developed by: Yehuda Katz (https://github.com/wycats/)
-  Inclusive of: handlebars*.js
-  Version used: 2.0.0
-  Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
-  Copyright (C) by Yehuda Katz (2011-2014)
-
 This project includes the software: jQuery JavaScript Library
   Available at: http://jquery.com/
   Developed by: The jQuery Foundation (http://jquery.org/)
@@ -249,18 +234,6 @@
     Available at http://sizzlejs.com
     Used under the MIT license
 
-This project includes the software: jQuery JavaScript Library
-  Available at: http://jquery.com/
-  Developed by: The jQuery Foundation (http://jquery.org/)
-  Inclusive of: jquery.js
-  Version used: 1.8.0
-  Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
-  Copyright (c) jQuery Foundation and other contributors (2012)
-  Includes code fragments from sizzle.js:
-    Copyright (c) The Dojo Foundation
-    Available at http://sizzlejs.com
-    Used under the MIT license
-
 This project includes the software: jQuery BBQ: Back Button & Query Library
   Available at: http://benalman.com/projects/jquery-bbq-plugin/
   Developed by: "Cowboy" Ben Alman (http://benalman.com/)
@@ -309,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)
@@ -350,19 +330,11 @@
       Arpad Borsos (2012)
     Used under the BSD 2-Clause license.
 
-This project includes the software: Swagger JS
-  Available at: https://github.com/swagger-api/swagger-js
-  Inclusive of: swagger.js
-  Version used: 2.1.6
-  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
-  Copyright (c) SmartBear Software (2015)
+This project includes the software: swagger
+  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-ui.js
-  Version used: 2.1.3
-  Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
-  Copyright (c) SmartBear Software (2015)
+This project includes the software: swagger-ui
+  Used under the following license: <no license info>
 
 This project includes the software: underscore.js
   Available at: http://underscorejs.org
@@ -372,14 +344,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
-  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)
@@ -388,13 +352,6 @@
   Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
   Copyright (c) Jon Rohan, James M. Greene (2014)
 
-This project includes the software: marked.js
-  Available at: https://github.com/chjj/marked
-  Developed by: Christopher Jeffrey (https://github.com/chjj)
-  Inclusive of: marked.js
-  Version used: 0.3.1
-  Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
-  Copyright (c) Christopher Jeffrey (2011-2014)
 
 ---------------------------------------------------