You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2014/07/18 18:34:10 UTC

[01/13] git commit: fix intermittent issue with apache rat

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master f0b8f296d -> 4fa68908b


fix intermittent issue with apache rat

those files are not always considered binary, but they are not directly used by code


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

Branch: refs/heads/master
Commit: 261afa87f535efbcfa3b16d3359d5591415617dd
Parents: 6325e50
Author: Andrea Turli <an...@gmail.com>
Authored: Wed Jul 16 17:08:38 2014 +0200
Committer: Andrea Turli <an...@gmail.com>
Committed: Wed Jul 16 17:08:38 2014 +0200

----------------------------------------------------------------------
 usage/launcher/src/test/resources/client_cert | Bin 595 -> 0 bytes
 usage/launcher/src/test/resources/server_cert | Bin 595 -> 0 bytes
 2 files changed, 0 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/261afa87/usage/launcher/src/test/resources/client_cert
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/resources/client_cert b/usage/launcher/src/test/resources/client_cert
deleted file mode 100644
index aea2a07..0000000
Binary files a/usage/launcher/src/test/resources/client_cert and /dev/null differ

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/261afa87/usage/launcher/src/test/resources/server_cert
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/resources/server_cert b/usage/launcher/src/test/resources/server_cert
deleted file mode 100644
index d8a172b..0000000
Binary files a/usage/launcher/src/test/resources/server_cert and /dev/null differ


[09/13] git commit: support effector parameters which are maps (@sjcorbett just did this also; now this commit simply adds a test for this)

Posted by he...@apache.org.
support effector parameters which are maps (@sjcorbett just did this also; now this commit simply adds a test for this)


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

Branch: refs/heads/master
Commit: cefcda3b862fbbc0ff0008daadf988f8c361522a
Parents: 8569120
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Jul 18 00:39:24 2014 -0400
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Jul 18 12:26:20 2014 -0400

----------------------------------------------------------------------
 .../brooklynnode/BrooklynNodeRestTest.java      | 55 +++++++++++++++++++-
 1 file changed, 54 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cefcda3b/usage/launcher/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java b/usage/launcher/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java
index 2cd97f0..2a3816b 100644
--- a/usage/launcher/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java
+++ b/usage/launcher/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java
@@ -1,7 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
 package brooklyn.entity.brooklynnode;
 
 import java.net.URI;
+import java.util.concurrent.Callable;
 
+import org.apache.http.client.HttpClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -19,12 +39,17 @@ import brooklyn.launcher.camp.SimpleYamlLauncher;
 import brooklyn.location.Location;
 import brooklyn.management.Task;
 import brooklyn.test.EntityTestUtils;
+import brooklyn.test.HttpTestUtils;
 import brooklyn.test.entity.TestApplication;
 import brooklyn.test.entity.TestEntity;
+import brooklyn.util.collections.Jsonya;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.config.ConfigBag;
+import brooklyn.util.http.HttpTool;
+import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.net.Urls;
+import brooklyn.util.repeat.Repeater;
 import brooklyn.util.time.Duration;
 
 import com.google.common.collect.Iterables;
@@ -40,7 +65,7 @@ public class BrooklynNodeRestTest {
     // so feels worth it to have as a unit test
     @Test
     public void testBrooklynNodeRestDeployAndMirror() {
-        SimpleYamlLauncher l = new SimpleYamlLauncherForTests();
+        final SimpleYamlLauncher l = new SimpleYamlLauncherForTests();
         try {
             TestApplication app = ApplicationBuilder.newManagedApp(TestApplication.class, l.getManagementContext());
 
@@ -84,6 +109,34 @@ public class BrooklynNodeRestTest {
             EntityTestUtils.assertAttributeEqualsEventually(mirror, TestEntity.NAME, "foo");
             log.info("Mirror successfully validated");
             
+            // also try deploying by invoking deploy through json
+            // (catch issues when effector params are map)
+            HttpClient client = HttpTool.httpClientBuilder().build();
+            HttpToolResponse result = HttpTool.httpPost(client, URI.create(Urls.mergePaths(uri.toString(), "/v1/applications/"+app.getId()+"/entities/"+bn.getId()
+                    +"/effectors/deployBlueprint")), 
+                MutableMap.of(com.google.common.net.HttpHeaders.CONTENT_TYPE, "application/json"), 
+                Jsonya.newInstance()
+                    .put("blueprintType", TestApplication.class.getName())
+                    .put("blueprintConfig", MutableMap.of(TestEntity.CONF_NAME.getName(), "foo"))
+                .toString().getBytes());
+            log.info("Deploy effector invoked, result: "+result);
+            HttpTestUtils.assertHealthyStatusCode( result.getResponseCode() );
+            
+            Repeater.create().every(Duration.millis(10)).until(new Callable<Boolean>() {
+                @Override
+                public Boolean call() throws Exception {
+                    return l.getManagementContext().getApplications().size() == 3;
+                }
+            }).limitTimeTo(Duration.TEN_SECONDS);
+            
+            apps = MutableSet.copyOf( l.getManagementContext().getApplications() );
+            apps.removeAll( MutableSet.of(app, newApp) );
+            Application newApp2 = Iterables.getOnlyElement(apps);
+            Entities.dumpInfo(newApp2);
+            
+            EntityTestUtils.assertAttributeEqualsEventually(newApp2, Attributes.SERVICE_UP, true);
+            Assert.assertEquals(newApp2.getConfig(TestEntity.CONF_NAME), "foo");
+            
         } finally {
             l.destroyAll();
         }


[05/13] git commit: tidies of Brooklyn node (warnings, logging)

Posted by he...@apache.org.
tidies of Brooklyn node (warnings, logging)


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

Branch: refs/heads/master
Commit: f94c2ae7c63b5170899c6c93587e22e709450140
Parents: f0b8f29
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Jul 17 12:40:58 2014 -0400
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Jul 18 09:53:52 2014 -0400

----------------------------------------------------------------------
 .../BrooklynNodeIntegrationTest.java            | 31 +++++++++++++++-----
 .../entity/brooklynnode/BrooklynNodeTest.java   |  4 ---
 2 files changed, 24 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f94c2ae7/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java b/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
index fa0fda2..ecc099f 100644
--- a/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
+++ b/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
@@ -42,7 +42,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.brooklynnode.BrooklynNode.DeployBlueprintEffector;
 import brooklyn.entity.brooklynnode.BrooklynNode.ExistingFileBehaviour;
 import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.event.feed.http.HttpValueFunctions;
 import brooklyn.event.feed.http.JsonFunctions;
 import brooklyn.location.Location;
 import brooklyn.location.LocationSpec;
@@ -53,8 +52,11 @@ import brooklyn.test.HttpTestUtils;
 import brooklyn.test.entity.TestApplication;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;
+import brooklyn.util.guava.Functionals;
 import brooklyn.util.http.HttpTool;
 import brooklyn.util.http.HttpToolResponse;
+import brooklyn.util.javalang.JavaClassNames;
+import brooklyn.util.os.Os;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Time;
 
@@ -86,10 +88,10 @@ public class BrooklynNodeIntegrationTest {
 
     @BeforeMethod(alwaysRun=true)
     public void setUp() throws Exception {
-        pseudoBrooklynPropertiesFile = File.createTempFile("brooklynnode-test", ".properties");
+        pseudoBrooklynPropertiesFile = Os.newTempFile("brooklynnode-test", ".properties");
         pseudoBrooklynPropertiesFile.delete();
 
-        pseudoBrooklynCatalogFile = File.createTempFile("brooklynnode-test", ".catalog");
+        pseudoBrooklynCatalogFile = Os.newTempFile("brooklynnode-test", ".catalog");
         pseudoBrooklynCatalogFile.delete();
 
         app = ApplicationBuilder.newManagedApp(TestApplication.class);
@@ -109,6 +111,7 @@ public class BrooklynNodeIntegrationTest {
         BrooklynNode brooklynNode = app.createAndManageChild(EntitySpec.create(BrooklynNode.class)
                 .configure(BrooklynNode.WEB_CONSOLE_BIND_ADDRESS, "127.0.0.1"));
         app.start(locs);
+        log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
         EntityTestUtils.assertAttributeEqualsEventually(brooklynNode, BrooklynNode.SERVICE_UP, true);
 
@@ -122,6 +125,7 @@ public class BrooklynNodeIntegrationTest {
                 .configure(BrooklynNode.NO_WEB_CONSOLE_AUTHENTICATION, true)
                 .configure(BrooklynNode.MANAGEMENT_USER, (String)null));
         app.start(locs);
+        log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
         EntityTestUtils.assertAttributeEqualsEventually(brooklynNode, BrooklynNode.SERVICE_UP, true);
 
@@ -137,6 +141,7 @@ public class BrooklynNodeIntegrationTest {
                 .configure(BrooklynNode.BROOKLYN_GLOBAL_PROPERTIES_REMOTE_PATH, pseudoBrooklynPropertiesFile.getAbsolutePath())
                 .configure(BrooklynNode.BROOKLYN_GLOBAL_PROPERTIES_CONTENTS, "abc=def"));
         app.start(locs);
+        log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
         assertEquals(Files.readLines(pseudoBrooklynPropertiesFile, Charsets.UTF_8), ImmutableList.of("abc=def"));
     }
@@ -148,6 +153,7 @@ public class BrooklynNodeIntegrationTest {
                 .configure(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_REMOTE_PATH, pseudoBrooklynPropertiesFile.getAbsolutePath())
                 .configure(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_CONTENTS, "abc=def"));
         app.start(locs);
+        log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
         assertEquals(Files.readLines(pseudoBrooklynPropertiesFile, Charsets.UTF_8), ImmutableList.of("abc=def"));
     }
@@ -162,6 +168,7 @@ public class BrooklynNodeIntegrationTest {
                 .configure(BrooklynNode.BROOKLYN_GLOBAL_PROPERTIES_REMOTE_PATH, pseudoBrooklynPropertiesFile.getAbsolutePath())
                 .configure(BrooklynNode.BROOKLYN_GLOBAL_PROPERTIES_URI, brooklynPropertiesSourceFile.toURI().toString()));
         app.start(locs);
+        log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
         assertEquals(Files.readLines(pseudoBrooklynPropertiesFile, Charsets.UTF_8), ImmutableList.of("abc=def"));
     }
@@ -173,6 +180,7 @@ public class BrooklynNodeIntegrationTest {
                 .configure(BrooklynNode.BROOKLYN_CATALOG_REMOTE_PATH, pseudoBrooklynCatalogFile.getAbsolutePath())
                 .configure(BrooklynNode.BROOKLYN_CATALOG_CONTENTS, "<catalog/>"));
         app.start(locs);
+        log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
         assertEquals(Files.readLines(pseudoBrooklynCatalogFile, Charsets.UTF_8), ImmutableList.of("<catalog/>"));
     }
@@ -187,6 +195,7 @@ public class BrooklynNodeIntegrationTest {
                 .configure(BrooklynNode.BROOKLYN_CATALOG_REMOTE_PATH, pseudoBrooklynCatalogFile.getAbsolutePath())
                 .configure(BrooklynNode.BROOKLYN_CATALOG_URI, brooklynCatalogSourceFile.toURI().toString()));
         app.start(locs);
+        log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
         assertEquals(Files.readLines(pseudoBrooklynCatalogFile, Charsets.UTF_8), ImmutableList.of("abc=def"));
     }
@@ -204,6 +213,7 @@ public class BrooklynNodeIntegrationTest {
                     .configure(BrooklynNode.RUN_DIR, tempDir.getAbsolutePath())
                     .configure(BrooklynNode.COPY_TO_RUNDIR, ImmutableMap.of(sourceFile.getAbsolutePath(), "${RUN}/myfile.txt")));
             app.start(locs);
+            log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
             assertEquals(Files.readLines(expectedFile, Charsets.UTF_8), ImmutableList.of("abc=def"));
         } finally {
@@ -231,6 +241,7 @@ public class BrooklynNodeIntegrationTest {
                     .configure(BrooklynNode.CLASSPATH, ImmutableList.of(classpathEntry1.getAbsolutePath(), classpathEntry2.getAbsolutePath()))
                     );
             app.start(locs);
+            log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
             assertEquals(Files.readLines(expectedFile1, Charsets.UTF_8), ImmutableList.of(content));
             assertEquals(Files.readLines(expectedFile2, Charsets.UTF_8), ImmutableList.of(content));
@@ -261,9 +272,10 @@ public class BrooklynNodeIntegrationTest {
     
             BrooklynNode brooklynNode = app.createAndManageChild(EntitySpec.create(BrooklynNode.class)
                     .configure(BrooklynNode.WEB_CONSOLE_BIND_ADDRESS, "127.0.0.1")
-                    .configure(BrooklynNode.SUGGESTED_RUN_DIR, tempDir.getAbsolutePath())
+                    .configure(BrooklynNode.RUN_DIR, tempDir.getAbsolutePath())
                     );
             app.start(locs);
+            log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
             assertEquals(Files.readLines(expectedFile1, Charsets.UTF_8), ImmutableList.of(content));
             assertEquals(Files.readLines(expectedFile2, Charsets.UTF_8), ImmutableList.of(content));
@@ -282,6 +294,7 @@ public class BrooklynNodeIntegrationTest {
                 .configure(BrooklynNode.WEB_CONSOLE_BIND_ADDRESS, "127.0.0.1")
                 .configure(BrooklynNode.HTTP_PORT, PortRanges.fromString("45000+")));
         app.start(locs);
+        log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
         Integer httpPort = brooklynNode.getAttribute(BrooklynNode.HTTP_PORT);
         URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
@@ -296,6 +309,7 @@ public class BrooklynNodeIntegrationTest {
                 .configure(BrooklynNode.NO_WEB_CONSOLE_AUTHENTICATION, true)
                 .configure(BrooklynNode.APP, BasicApplicationImpl.class.getName()));
         app.start(locs);
+        log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
         URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
         String apps = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications");
@@ -308,6 +322,7 @@ public class BrooklynNodeIntegrationTest {
         BrooklynNode brooklynNode = app.createAndManageChild(EntitySpec.create(BrooklynNode.class)
                 .configure(BrooklynNode.NO_WEB_CONSOLE_AUTHENTICATION, true));
         app.start(locs);
+        log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
         
         final String id = brooklynNode.invoke(BrooklynNode.DEPLOY_BLUEPRINT, ConfigBag.newInstance()
             .configure(DeployBlueprintEffector.BLUEPRINT_TYPE, BasicApplication.class.getName())
@@ -342,6 +357,7 @@ public class BrooklynNodeIntegrationTest {
                     .configure(BrooklynNode.APP, BasicApplicationImpl.class.getName())
                     .configure(BrooklynNode.LOCATIONS, "named:mynamedloc"));
             app.start(locs);
+            log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
             URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
 
@@ -388,6 +404,7 @@ public class BrooklynNodeIntegrationTest {
                         //                .configure(BrooklynNode.HTTP_PORT, PortRanges.fromString("45000+"))
                 );
             app.start(locs);
+            log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
             URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
             Assert.assertTrue(webConsoleUri.toString().startsWith("https://"), "web console not https: "+webConsoleUri);
@@ -411,7 +428,7 @@ public class BrooklynNodeIntegrationTest {
     }
 
     private <T> T parseJson(String json, List<String> elements, Class<T> clazz) {
-        Function<String, T> func = HttpValueFunctions.chain(
+        Function<String, T> func = Functionals.chain(
                 JsonFunctions.asJson(),
                 JsonFunctions.walk(elements),
                 JsonFunctions.cast(clazz));
@@ -419,9 +436,9 @@ public class BrooklynNodeIntegrationTest {
     }
 
     private <T> List<T> parseJsonList(String json, List<String> elements, Class<T> clazz) {
-        Function<String, List<T>> func = HttpValueFunctions.chain(
+        Function<String, List<T>> func = Functionals.chain(
                 JsonFunctions.asJson(),
-                JsonFunctions.forEach(HttpValueFunctions.chain(
+                JsonFunctions.forEach(Functionals.chain(
                         JsonFunctions.walk(elements),
                         JsonFunctions.cast(clazz))));
         return func.apply(json);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f94c2ae7/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeTest.java b/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeTest.java
index 7c96416..8f7d561 100644
--- a/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeTest.java
+++ b/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeTest.java
@@ -20,7 +20,6 @@ package brooklyn.entity.brooklynnode;
 
 import static org.testng.Assert.assertTrue;
 
-import java.io.File;
 import java.util.List;
 
 import org.testng.annotations.AfterMethod;
@@ -38,9 +37,6 @@ public class BrooklynNodeTest {
 
     // TODO Need test for copying/setting classpath
     
-    private static final File BROOKLYN_PROPERTIES_PATH = new File(System.getProperty("user.home")+"/.brooklyn/brooklyn.properties");
-    private static final File BROOKLYN_PROPERTIES_BAK_PATH = new File(BROOKLYN_PROPERTIES_PATH+".test.bak");
-    
     private TestApplication app;
     private SshMachineLocation loc;
 


[04/13] git commit: promote snake-yaml dependency and Yamls utility to utils package, and use yaml parsing for map coercion to be much more flexible in terms of string-to-map coercion

Posted by he...@apache.org.
promote snake-yaml dependency and Yamls utility to utils package, and use yaml parsing for map coercion to be much more flexible in terms of string-to-map coercion


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

Branch: refs/heads/master
Commit: a4f7a4c0e3db5220e936bcffb5d52c3731b22175
Parents: f94c2ae
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Jul 17 23:29:12 2014 -0400
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Jul 18 09:53:52 2014 -0400

----------------------------------------------------------------------
 camp/camp-base/pom.xml                          |   5 -
 .../java/io/brooklyn/camp/spi/pdp/Artifact.java |   3 +-
 .../camp/spi/pdp/ArtifactRequirement.java       |   3 +-
 .../java/io/brooklyn/camp/spi/pdp/Service.java  |   3 +-
 .../camp/spi/pdp/ServiceCharacteristic.java     |   3 +-
 .../brooklyn/camp/spi/resolve/PdpProcessor.java |   2 +-
 .../main/java/io/brooklyn/util/yaml/Yamls.java  |  89 +-------------
 .../pdp/DeploymentPlanToyInterpreterTest.java   |   2 +-
 .../java/brooklyn/util/flags/TypeCoercions.java |  85 +++++++++-----
 .../util/internal/TypeCoercionsTest.java        |  72 ++++++++++--
 usage/launcher/pom.xml                          |   7 ++
 utils/common/pom.xml                            |   5 +
 .../src/main/java/brooklyn/util/yaml/Yamls.java | 115 +++++++++++++++++++
 13 files changed, 253 insertions(+), 141 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/pom.xml
----------------------------------------------------------------------
diff --git a/camp/camp-base/pom.xml b/camp/camp-base/pom.xml
index b80ef0c..e8638b1 100644
--- a/camp/camp-base/pom.xml
+++ b/camp/camp-base/pom.xml
@@ -57,11 +57,6 @@
             <artifactId>commons-compress</artifactId>
             <version>${commons-compress.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.yaml</groupId>
-            <artifactId>snakeyaml</artifactId>
-            <version>${snakeyaml.version}</version>
-        </dependency>
         
             <!-- just for logging, not exported -->
             <!-- 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java
index 72f356e..697a3ba 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java
@@ -18,8 +18,6 @@
  */
 package io.brooklyn.camp.spi.pdp;
 
-import io.brooklyn.util.yaml.Yamls;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -27,6 +25,7 @@ import java.util.Map;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 
 import brooklyn.util.collections.MutableMap;
+import brooklyn.util.yaml.Yamls;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java
index 50f5335..da9936a 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java
@@ -18,13 +18,12 @@
  */
 package io.brooklyn.camp.spi.pdp;
 
-import io.brooklyn.util.yaml.Yamls;
-
 import java.util.Map;
 
 import org.apache.commons.lang3.builder.ToStringBuilder;
 
 import brooklyn.util.collections.MutableMap;
+import brooklyn.util.yaml.Yamls;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java
index 241b80c..5921176 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java
@@ -18,8 +18,6 @@
  */
 package io.brooklyn.camp.spi.pdp;
 
-import io.brooklyn.util.yaml.Yamls;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -27,6 +25,7 @@ import java.util.Map;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 
 import brooklyn.util.collections.MutableMap;
+import brooklyn.util.yaml.Yamls;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java
index cc5227d..8b27e2a 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java
@@ -18,13 +18,12 @@
  */
 package io.brooklyn.camp.spi.pdp;
 
-import io.brooklyn.util.yaml.Yamls;
-
 import java.util.Map;
 
 import org.apache.commons.lang3.builder.ToStringBuilder;
 
 import brooklyn.util.collections.MutableMap;
+import brooklyn.util.yaml.Yamls;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpProcessor.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpProcessor.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpProcessor.java
index 897ff23..3252aaf 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpProcessor.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpProcessor.java
@@ -27,7 +27,6 @@ import io.brooklyn.camp.spi.pdp.DeploymentPlan;
 import io.brooklyn.camp.spi.pdp.Service;
 import io.brooklyn.camp.spi.resolve.interpret.PlanInterpretationContext;
 import io.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode;
-import io.brooklyn.util.yaml.Yamls;
 
 import java.io.InputStream;
 import java.io.Reader;
@@ -41,6 +40,7 @@ import org.apache.commons.compress.archivers.ArchiveStreamFactory;
 import org.yaml.snakeyaml.error.YAMLException;
 
 import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.yaml.Yamls;
 
 import com.google.common.annotations.VisibleForTesting;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/util/yaml/Yamls.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/util/yaml/Yamls.java b/camp/camp-base/src/main/java/io/brooklyn/util/yaml/Yamls.java
index d275ce8..44974f0 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/util/yaml/Yamls.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/util/yaml/Yamls.java
@@ -18,90 +18,7 @@
  */
 package io.brooklyn.util.yaml;
 
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.Iterables;
-
-public class Yamls {
-
-    private static final Logger log = LoggerFactory.getLogger(Yamls.class);
-    
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static <T> T getAs(Object x, Class<T> type) {
-        if (x==null) return null;
-        if (x instanceof Iterable || x instanceof Iterator) {
-            List result = new ArrayList();
-            Iterator xi;
-            if (Iterator.class.isAssignableFrom(x.getClass())) {
-                xi = (Iterator)x;
-            } else {
-                xi = ((Iterable)x).iterator();
-            }
-            while (xi.hasNext()) {
-                    result.add( xi.next() );
-            }
-            if (type.isAssignableFrom(Iterable.class)) return (T)result;
-            if (type.isAssignableFrom(Iterator.class)) return (T)result.iterator();
-            if (type.isAssignableFrom(List.class)) return (T)result;
-            x = Iterables.getOnlyElement(result);
-        }
-        // TODO more coercion?
-        return (T)x;
-    }
-
-    @SuppressWarnings("rawtypes")
-    public static void dump(int depth, Object r) {
-        if (r instanceof Iterable) {
-            for (Object ri : ((Iterable)r))
-                dump(depth+1, ri);
-        } else if (r instanceof Map) {
-            for (Object re: ((Map)r).entrySet()) {
-                for (int i=0; i<depth; i++) System.out.print(" ");
-                System.out.println(((Entry)re).getKey()+":");
-                dump(depth+1, ((Entry)re).getValue());
-            }
-        } else {
-            for (int i=0; i<depth; i++) System.out.print(" ");
-            if (r==null) System.out.println("<null>");
-            else System.out.println("<"+r.getClass().getSimpleName()+">"+" "+r);
-        }
-    }
-
-    /** simplifies new Yaml().loadAll, and converts to list to prevent single-use iterable bug in yaml */
-    @SuppressWarnings("unchecked")
-    public static Iterable<Object> parseAll(String yaml) {
-        Iterable<Object> result = new org.yaml.snakeyaml.Yaml().loadAll(yaml);
-        return (List<Object>) getAs(result, List.class);
-    }
-
-    /** as {@link #parseAll(String)} */
-    @SuppressWarnings("unchecked")
-    public static Iterable<Object> parseAll(Reader yaml) {
-        Iterable<Object> result = new org.yaml.snakeyaml.Yaml().loadAll(yaml);
-        return (List<Object>) getAs(result, List.class);
-    }
-
-    public static Object removeMultinameAttribute(Map<String,Object> obj, String ...equivalentNames) {
-        Object result = null;
-        for (String name: equivalentNames) {
-            Object candidate = obj.remove(name);
-            if (candidate!=null) {
-                if (result==null) result = candidate;
-                else if (!result.equals(candidate)) {
-                    log.warn("Different values for attributes "+Arrays.toString(equivalentNames)+"; " +
-                    		"preferring '"+result+"' to '"+candidate+"'");
-                }
-            }
-        }
-        return result;
-    }
+/** @deprecated since 0.7.0 use {@link brooklyn.util.yaml.Yamls} */
+@Deprecated
+public class Yamls extends brooklyn.util.yaml.Yamls {
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/test/java/io/brooklyn/camp/spi/pdp/DeploymentPlanToyInterpreterTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/test/java/io/brooklyn/camp/spi/pdp/DeploymentPlanToyInterpreterTest.java b/camp/camp-base/src/test/java/io/brooklyn/camp/spi/pdp/DeploymentPlanToyInterpreterTest.java
index de922a1..29f8abf 100644
--- a/camp/camp-base/src/test/java/io/brooklyn/camp/spi/pdp/DeploymentPlanToyInterpreterTest.java
+++ b/camp/camp-base/src/test/java/io/brooklyn/camp/spi/pdp/DeploymentPlanToyInterpreterTest.java
@@ -21,7 +21,6 @@ package io.brooklyn.camp.spi.pdp;
 import io.brooklyn.camp.BasicCampPlatform;
 import io.brooklyn.camp.spi.resolve.PlanInterpreter.PlanInterpreterAdapter;
 import io.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode;
-import io.brooklyn.util.yaml.Yamls;
 
 import java.util.Map;
 
@@ -32,6 +31,7 @@ import org.testng.annotations.Test;
 
 import brooklyn.util.stream.Streams;
 import brooklyn.util.text.Strings;
+import brooklyn.util.yaml.Yamls;
 
 @Test
 public class DeploymentPlanToyInterpreterTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
index 1ee45ae..7e92ed5 100644
--- a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
+++ b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
@@ -18,7 +18,9 @@
  */
 package brooklyn.util.flags;
 
-import java.io.IOException;
+import groovy.lang.Closure;
+import groovy.time.TimeDuration;
+
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -39,23 +41,9 @@ import java.util.concurrent.atomic.AtomicLong;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.GuardedBy;
 
-import org.codehaus.jackson.map.ObjectMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.CaseFormat;
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Splitter;
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Table;
-import com.google.common.net.HostAndPort;
-import com.google.common.primitives.Primitives;
-import com.google.common.reflect.TypeToken;
-
 import brooklyn.entity.basic.ClosureEntityFactory;
 import brooklyn.entity.basic.ConfigurableEntityFactory;
 import brooklyn.entity.basic.ConfigurableEntityFactoryFromEntityFactory;
@@ -70,10 +58,23 @@ import brooklyn.util.net.Cidr;
 import brooklyn.util.net.Networking;
 import brooklyn.util.net.UserAndHostAndPort;
 import brooklyn.util.text.StringEscapes.JavaStringEscapes;
+import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
-import groovy.lang.Closure;
-import groovy.time.TimeDuration;
+import brooklyn.util.yaml.Yamls;
 
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Table;
+import com.google.common.net.HostAndPort;
+import com.google.common.primitives.Primitives;
+import com.google.common.reflect.TypeToken;
+
+@SuppressWarnings("rawtypes")
 public class TypeCoercions {
 
     private static final Logger log = LoggerFactory.getLogger(TypeCoercions.class);
@@ -103,7 +104,7 @@ public class TypeCoercions {
     }
 
     /** @see #coerce(Object, Class) */
-    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @SuppressWarnings({ "unchecked" })
     public static <T> T coerce(Object value, TypeToken<T> targetTypeToken) {
         if (value==null) return null;
         // does not actually cast generified contents; that is left to the caller
@@ -433,12 +434,14 @@ public class TypeCoercions {
             }
         });
         registerAdapter(Collection.class, Set.class, new Function<Collection,Set>() {
+            @SuppressWarnings("unchecked")
             @Override
             public Set apply(Collection input) {
                 return new LinkedHashSet(input);
             }
         });
         registerAdapter(Collection.class, List.class, new Function<Collection,List>() {
+            @SuppressWarnings("unchecked")
             @Override
             public List apply(Collection input) {
                 return new ArrayList(input);
@@ -485,12 +488,14 @@ public class TypeCoercions {
             }
         });
         registerAdapter(Closure.class, ConfigurableEntityFactory.class, new Function<Closure,ConfigurableEntityFactory>() {
+            @SuppressWarnings("unchecked")
             @Override
             public ConfigurableEntityFactory apply(Closure input) {
                 return new ClosureEntityFactory(input);
             }
         });
         registerAdapter(EntityFactory.class, ConfigurableEntityFactory.class, new Function<EntityFactory,ConfigurableEntityFactory>() {
+            @SuppressWarnings("unchecked")
             @Override
             public ConfigurableEntityFactory apply(EntityFactory input) {
                 if (input instanceof ConfigurableEntityFactory) return (ConfigurableEntityFactory)input;
@@ -498,6 +503,7 @@ public class TypeCoercions {
             }
         });
         registerAdapter(Closure.class, EntityFactory.class, new Function<Closure,EntityFactory>() {
+            @SuppressWarnings("unchecked")
             @Override
             public EntityFactory apply(Closure input) {
                 return new ClosureEntityFactory(input);
@@ -530,6 +536,7 @@ public class TypeCoercions {
             }
         });
         registerAdapter(Object.class, TimeDuration.class, new Function<Object,TimeDuration>() {
+            @SuppressWarnings("deprecation")
             @Override
             public TimeDuration apply(final Object input) {
                 log.warn("deprecated automatic coercion of Object to TimeDuration (set breakpoint in TypeCoercions to inspect, convert to Duration)");
@@ -627,20 +634,40 @@ public class TypeCoercions {
         registerAdapter(String.class, Map.class, new Function<String,Map>() {
             @Override
             public Map apply(final String input) {
-                // Auto-detect JSON. This allows complex data structures to be received over the REST API.
+                Exception error = null;
+                
+                // first try wrapping in braces if needed
+                if (!input.trim().startsWith("{")) {
+                    try {
+                        return apply("{ "+input+" }");
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        // prefer this error
+                        error = e;
+                        // fall back to parsing without braces, e.g. if it's multiline
+                    }
+                }
+
                 try {
-                    if (!input.isEmpty() && input.charAt(0) == '{') {
-                        return new ObjectMapper().readValue(input, Map.class);
+                    return Yamls.getAs( Yamls.parseAll(input), Map.class );
+                } catch (Exception e) {
+                    Exceptions.propagateIfFatal(e);
+                    if (error!=null && input.indexOf('\n')==-1) {
+                        // prefer the original error if it wasn't braced and wasn't multiline
+                        e = error;
                     }
-                } catch (IOException e) {
-                    // just fall through to the map parsing
+                    throw new IllegalArgumentException("Cannot parse string as map with flexible YAML parsing; "+
+                        (e instanceof ClassCastException ? "yaml treats it as a string" : 
+                        (e instanceof IllegalArgumentException && Strings.isNonEmpty(e.getMessage())) ? e.getMessage() :
+                        ""+e) );
                 }
-                
-                // TODO would be nice to accept YAML for complex data structures too, but it's not as simple as JSON to auto-detect.
-                
-                // Simple map parsing - supports "key1=value1,key2=value2" style input
-                // TODO we should respect quoted strings etc
-                return ImmutableMap.copyOf(Splitter.on(",").trimResults().omitEmptyStrings().withKeyValueSeparator("=").split(input));
+
+                // NB: previously we supported this also, when we did json above;
+                // yaml support is better as it supports quotes (and better than json because it allows dropping quotes)
+                // snake-yaml, our parser, also accepts key=value -- although i'm not sure this is strictly yaml compliant;
+                // our tests will catch it if snake behaviour changes, and we can reinstate this
+                // (but note it doesn't do quotes; see http://code.google.com/p/guava-libraries/issues/detail?id=412 for that):
+//                return ImmutableMap.copyOf(Splitter.on(",").trimResults().omitEmptyStrings().withKeyValueSeparator("=").split(input));
             }
         });
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java b/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java
index 05b4c72..6efffa2 100644
--- a/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java
+++ b/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java
@@ -32,17 +32,17 @@ import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.util.flags.ClassCoercionException;
+import brooklyn.util.flags.TypeCoercions;
+import brooklyn.util.text.StringPredicates;
+
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.reflect.TypeToken;
 
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.util.flags.ClassCoercionException;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.text.StringPredicates;
-
 public class TypeCoercionsTest {
 
     private static final Logger log = LoggerFactory.getLogger(TypeCoercionsTest.class);
@@ -191,15 +191,65 @@ public class TypeCoercionsTest {
     }
 
     @Test
-    public void testStringToMapCoercion() {
-        Map<?,?> s = TypeCoercions.coerce("a=1,b=2,c=3", Map.class);
-        Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", "2", "c", "3"));
+    public void testJsonStringToMapCoercion() {
+        Map<?,?> s = TypeCoercions.coerce("{ \"a\" : \"1\", b : 2 }", Map.class);
+        Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", 2));
     }
 
     @Test
-    public void testJsonStringToMapCoercion() {
-        Map<?,?> s = TypeCoercions.coerce("{ \"a\" : \"1\", \"b\" : \"2\", \"c\" : \"3\" }", Map.class);
-        Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", "2", "c", "3"));
+    public void testJsonStringWithoutQuotesToMapCoercion() {
+        Map<?,?> s = TypeCoercions.coerce("{ a : 1 }", Map.class);
+        Assert.assertEquals(s, ImmutableMap.of("a", 1));
+    }
+
+    @Test
+    public void testJsonComplexTypesToMapCoercion() {
+        Map<?,?> s = TypeCoercions.coerce("{ a : [1, \"2\", '\"3\"'], b: { c: d, 'e': \"f\" } }", Map.class);
+        Assert.assertEquals(s, ImmutableMap.of("a", ImmutableList.<Object>of(1, "2", "\"3\""), 
+            "b", ImmutableMap.of("c", "d", "e", "f")));
+    }
+
+    @Test
+    public void testJsonStringWithoutBracesToMapCoercion() {
+        Map<?,?> s = TypeCoercions.coerce("a : 1", Map.class);
+        Assert.assertEquals(s, ImmutableMap.of("a", 1));
+    }
+
+    @Test
+    public void testJsonStringWithoutBracesWithMultipleToMapCoercion() {
+        Map<?,?> s = TypeCoercions.coerce("a : 1, b : 2", Map.class);
+        Assert.assertEquals(s, ImmutableMap.of("a", 1, "b", 2));
+    }
+
+    @Test
+    public void testKeyEqualsValueStringToMapCoercion() {
+        Map<?,?> s = TypeCoercions.coerce("a=1,b=2", Map.class);
+        Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", "2"));
+    }
+
+    @Test(expectedExceptions=IllegalArgumentException.class)
+    public void testJsonStringWithoutBracesOrSpaceDisallowedAsMapCoercion() {
+        // yaml requires spaces after the colon
+        Map<?,?> s = TypeCoercions.coerce("a:1,b:2", Map.class);
+        Assert.assertEquals(s, ImmutableMap.of("a", 1, "b", 2));
+    }
+    
+    @Test
+    public void testEqualsInBracesMapCoercion() {
+        Map<?,?> s = TypeCoercions.coerce("{ a = 1, b = '2' }", Map.class);
+        Assert.assertEquals(s, ImmutableMap.of("a", 1, "b", "2"));
+    }
+
+    @Test
+    public void testKeyEqualsOrColonValueWithBracesStringToMapCoercion() {
+        Map<?,?> s = TypeCoercions.coerce("{ a=1, b: 2 }", Map.class);
+        Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", 2));
+    }
+
+    @Test
+    public void testKeyEqualsOrColonValueWithoutBracesStringToMapCoercion() {
+        Map<?,?> s = TypeCoercions.coerce("a=1, b: 2", Map.class);
+        Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", 2));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/usage/launcher/pom.xml
----------------------------------------------------------------------
diff --git a/usage/launcher/pom.xml b/usage/launcher/pom.xml
index 57d3e24..1fb4fcd 100644
--- a/usage/launcher/pom.xml
+++ b/usage/launcher/pom.xml
@@ -86,6 +86,13 @@
         </dependency>
         <dependency>
             <groupId>io.brooklyn</groupId>
+            <artifactId>brooklyn-software-base</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.brooklyn</groupId>
             <artifactId>brooklyn-software-webapp</artifactId>
             <version>${project.version}</version>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/utils/common/pom.xml
----------------------------------------------------------------------
diff --git a/utils/common/pom.xml b/utils/common/pom.xml
index 24dec9f..54de72a 100644
--- a/utils/common/pom.xml
+++ b/utils/common/pom.xml
@@ -56,6 +56,11 @@
             <artifactId>commons-io</artifactId>
             <version>${commons-io.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.yaml</groupId>
+            <artifactId>snakeyaml</artifactId>
+            <version>${snakeyaml.version}</version>
+        </dependency>
         
         <dependency>
             <groupId>org.testng</groupId>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java b/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java
new file mode 100644
index 0000000..a22432f
--- /dev/null
+++ b/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.util.yaml;
+
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Iterables;
+
+public class Yamls {
+
+    private static final Logger log = LoggerFactory.getLogger(Yamls.class);
+
+    /** returns the given yaml object (map or list or primitive) as the given yaml-supperted type 
+     * (map or list or primitive e.g. string, number, boolean).
+     * <p>
+     * if the object is an iterable containing a single element, and the type is not an iterable,
+     * this will attempt to unwrap it.
+     * 
+     * @throws IllegalArgumentException if the input is an iterable not containing a single element,
+     *   and the cast is requested to a non-iterable type 
+     * @throws ClassCastException if cannot be casted */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <T> T getAs(Object x, Class<T> type) {
+        if (x==null) return null;
+        if (x instanceof Iterable || x instanceof Iterator) {
+            List result = new ArrayList();
+            Iterator xi;
+            if (Iterator.class.isAssignableFrom(x.getClass())) {
+                xi = (Iterator)x;
+            } else {
+                xi = ((Iterable)x).iterator();
+            }
+            while (xi.hasNext()) {
+                result.add( xi.next() );
+            }
+            if (type.isAssignableFrom(Iterable.class)) return (T)result;
+            if (type.isAssignableFrom(Iterator.class)) return (T)result.iterator();
+            if (type.isAssignableFrom(List.class)) return (T)result;
+            x = Iterables.getOnlyElement(result);
+        }
+        return (T)x;
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static void dump(int depth, Object r) {
+        if (r instanceof Iterable) {
+            for (Object ri : ((Iterable)r))
+                dump(depth+1, ri);
+        } else if (r instanceof Map) {
+            for (Object re: ((Map)r).entrySet()) {
+                for (int i=0; i<depth; i++) System.out.print(" ");
+                System.out.println(((Entry)re).getKey()+":");
+                dump(depth+1, ((Entry)re).getValue());
+            }
+        } else {
+            for (int i=0; i<depth; i++) System.out.print(" ");
+            if (r==null) System.out.println("<null>");
+            else System.out.println("<"+r.getClass().getSimpleName()+">"+" "+r);
+        }
+    }
+
+    /** simplifies new Yaml().loadAll, and converts to list to prevent single-use iterable bug in yaml */
+    @SuppressWarnings("unchecked")
+    public static Iterable<Object> parseAll(String yaml) {
+        Iterable<Object> result = new org.yaml.snakeyaml.Yaml().loadAll(yaml);
+        return (List<Object>) getAs(result, List.class);
+    }
+
+    /** as {@link #parseAll(String)} */
+    @SuppressWarnings("unchecked")
+    public static Iterable<Object> parseAll(Reader yaml) {
+        Iterable<Object> result = new org.yaml.snakeyaml.Yaml().loadAll(yaml);
+        return (List<Object>) getAs(result, List.class);
+    }
+
+    public static Object removeMultinameAttribute(Map<String,Object> obj, String ...equivalentNames) {
+        Object result = null;
+        for (String name: equivalentNames) {
+            Object candidate = obj.remove(name);
+            if (candidate!=null) {
+                if (result==null) result = candidate;
+                else if (!result.equals(candidate)) {
+                    log.warn("Different values for attributes "+Arrays.toString(equivalentNames)+"; " +
+                    		"preferring '"+result+"' to '"+candidate+"'");
+                }
+            }
+        }
+        return result;
+    }
+}


[11/13] git commit: This closes #76

Posted by he...@apache.org.
This closes #76


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

Branch: refs/heads/master
Commit: 671fb174ca8033824e772af815e78107e4ed1f66
Parents: f0b8f29 fe0d9d3
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Jul 18 12:31:36 2014 -0400
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Jul 18 12:31:36 2014 -0400

----------------------------------------------------------------------
 .../internal/BrooklynGarbageCollector.java      |  32 +-
 .../util/task/BasicTaskExecutionTest.groovy     | 370 ---------------
 .../util/task/BasicTaskExecutionTest.java       | 460 +++++++++++++++++++
 .../util/task/ScheduledExecutionTest.groovy     | 148 ------
 .../util/task/ScheduledExecutionTest.java       | 175 +++++++
 .../task/SingleThreadedSchedulerTest.groovy     | 154 -------
 .../util/task/SingleThreadedSchedulerTest.java  | 192 ++++++++
 7 files changed, 853 insertions(+), 678 deletions(-)
----------------------------------------------------------------------



[03/13] git commit: Converts task tests from groovy to java

Posted by he...@apache.org.
Converts task tests from groovy to java


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

Branch: refs/heads/master
Commit: 35363d613d4b09caa1bd067f0a2b47e10354611c
Parents: 6325e50
Author: Aled Sage <al...@gmail.com>
Authored: Wed Jul 16 23:47:35 2014 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Jul 17 11:28:44 2014 +0100

----------------------------------------------------------------------
 .../util/task/BasicTaskExecutionTest.groovy     | 370 ---------------
 .../util/task/BasicTaskExecutionTest.java       | 460 +++++++++++++++++++
 .../util/task/ScheduledExecutionTest.groovy     | 148 ------
 .../util/task/ScheduledExecutionTest.java       | 175 +++++++
 .../task/SingleThreadedSchedulerTest.groovy     | 154 -------
 .../util/task/SingleThreadedSchedulerTest.java  | 192 ++++++++
 6 files changed, 827 insertions(+), 672 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35363d61/core/src/test/java/brooklyn/util/task/BasicTaskExecutionTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/BasicTaskExecutionTest.groovy b/core/src/test/java/brooklyn/util/task/BasicTaskExecutionTest.groovy
deleted file mode 100644
index 1d104a8..0000000
--- a/core/src/test/java/brooklyn/util/task/BasicTaskExecutionTest.groovy
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task;
-
-import static org.testng.Assert.*
-
-import java.util.concurrent.Callable
-import java.util.concurrent.CancellationException
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod
-import org.testng.annotations.Test
-
-import brooklyn.management.ExecutionManager
-import brooklyn.management.Task
-import brooklyn.test.TestUtils
-
-import com.google.common.base.Throwables
-
-/**
- * Test the operation of the {@link BasicTask} class.
- *
- * TODO clarify test purpose
- */
-public class BasicTaskExecutionTest {
-    private static final Logger log = LoggerFactory.getLogger(BasicTaskExecutionTest.class)
- 
-    private static final int TIMEOUT_MS = 10*1000
-    
-    private BasicExecutionManager em;
-    private Map data;
-
-    @BeforeMethod
-    public void setUp() {
-        em = new BasicExecutionManager("mycontext");
-//        assertTrue em.allTasks.isEmpty()
-        data = Collections.synchronizedMap(new HashMap())
-        data.clear()
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (em != null) em.shutdownNow();
-    }
-    
-    @Test
-    public void runSimpleBasicTask() {
-        data.clear()
-        BasicTask t = [ { data.put(1, "b") } ]
-        data.put(1, "a")
-        BasicTask t2 = em.submit tag:"A", t
-        assertEquals("a", t.get())
-        assertEquals("b", data.get(1))
-    }
-    
-    @Test
-    public void runSimpleRunnable() {
-        data.clear()
-        data.put(1, "a")
-        BasicTask t = em.submit tag:"A", new Runnable() { public void run() { data.put(1, "b") } }
-        assertEquals(null, t.get())
-        assertEquals("b", data.get(1))
-    }
-
-    @Test
-    public void runSimpleCallable() {
-        data.clear()
-        data.put(1, "a")
-        BasicTask t = em.submit tag:"A", new Callable() { public Object call() { data.put(1, "b") } }
-        assertEquals("a", t.get())
-        assertEquals("b", data.get(1))
-    }
-
-    @Test
-    public void runBasicTaskWithWaits() {
-        CountDownLatch signalStarted = new CountDownLatch(1);
-        CountDownLatch allowCompletion = new CountDownLatch(1);
-        data.clear()
-        BasicTask t = [ {
-            def result = data.put(1, "b")
-            signalStarted.countDown();
-            assertTrue(allowCompletion.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-            result
-        } ]
-        data.put(1, "a")
-
-        BasicTask t2 = em.submit tag:"A", t
-        assertEquals(t, t2)
-        assertFalse(t.isDone())
-        
-        assertTrue(signalStarted.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        assertEquals("b", data.get(1))
-        assertFalse(t.isDone())
-        
-        log.debug "runBasicTaskWithWaits, BasicTask status: {}", t.getStatusDetail(false)
-        
-        TestUtils.executeUntilSucceeds { t.getStatusDetail(false).toLowerCase().contains("waiting") }
-        // "details="+t.getStatusDetail(false))
-        
-        allowCompletion.countDown();
-        assertEquals("a", t.get())
-    }
-
-    @Test
-    public void runMultipleBasicTasks() {
-        data.clear()
-        data.put(1, 1)
-        BasicExecutionManager em = []
-        2.times { em.submit tag:"A", new BasicTask({ synchronized(data) { data.put(1, data.get(1)+1) } }) }
-        2.times { em.submit tag:"B", new BasicTask({ synchronized(data) { data.put(1, data.get(1)+1) } }) }
-        int total = 0;
-        em.getTaskTags().each {
-                log.debug "tag {}", it
-                em.getTasksWithTag(it).each {
-                    log.debug "BasicTask {}, has {}", it, it.get()
-                    total += it.get()
-                }
-            }
-        assertEquals(10, total)
-        //now that all have completed:
-        assertEquals(5, data.get(1))
-    }
-
-    @Test
-    public void runMultipleBasicTasksMultipleTags() {
-        data.clear()
-        data.put(1, 1)
-        Collection<Task> tasks = []
-        tasks += em.submit tag:"A", new BasicTask({ synchronized(data) { data.put(1, data.get(1)+1) } })
-        tasks += em.submit tags:["A","B"], new BasicTask({ synchronized(data) { data.put(1, data.get(1)+1) } })
-        tasks += em.submit tags:["B","C"], new BasicTask({ synchronized(data) { data.put(1, data.get(1)+1) } })
-        tasks += em.submit tags:["D"], new BasicTask({ synchronized(data) { data.put(1, data.get(1)+1) } })
-        int total = 0;
-
-        tasks.each { Task t ->
-                log.debug "BasicTask {}, has {}", t, t.get()
-                total += t.get()
-            }
-        assertEquals(10, total)
- 
-        //now that all have completed:
-        assertEquals data.get(1), 5
-        assertEquals em.getTasksWithTag("A").size(), 2
-        assertEquals em.getTasksWithAnyTag(["A"]).size(), 2
-        assertEquals em.getTasksWithAllTags(["A"]).size(), 2
-
-        assertEquals em.getTasksWithAnyTag(["A", "B"]).size(), 3
-        assertEquals em.getTasksWithAllTags(["A", "B"]).size(), 1
-        assertEquals em.getTasksWithAllTags(["B", "C"]).size(), 1
-        assertEquals em.getTasksWithAnyTag(["A", "D"]).size(), 3
-    }
-
-    @Test
-    public void testGetTaskById() {
-        Task t = new BasicTask({ /*no-op*/ })
-        em.submit tag:"A",t
-        assertEquals(em.getTask(t.id), t);
-    }
-
-    @Test
-    public void testRetrievingTasksWithTagsReturnsExpectedTask() {
-        Task t = new BasicTask({ /*no-op*/ })
-        em.submit tag:"A",t
-        t.get();
-
-        assertEquals(em.getTasksWithTag("A"), [t]);
-        assertEquals(em.getTasksWithAnyTag(["A"]), [t]);
-        assertEquals(em.getTasksWithAnyTag(["A","B"]), [t]);
-        assertEquals(em.getTasksWithAllTags(["A"]), [t]);
-    }
-
-    @Test
-    public void testRetrievingTasksWithTagsExcludesNonMatchingTasks() {
-        Task t = new BasicTask({ /*no-op*/ })
-        em.submit tag:"A",t
-        t.get();
-
-        assertEquals(em.getTasksWithTag("B"), []);
-        assertEquals(em.getTasksWithAnyTag(["B"]), []);
-        assertEquals(em.getTasksWithAllTags(["A","B"]), []);
-    }
-    
-    @Test
-    public void testRetrievingTasksWithMultipleTags() {
-        Task t = new BasicTask({ /*no-op*/ })
-        em.submit tags:["A","B"], t
-        t.get();
-
-        assertEquals(em.getTasksWithTag("A"), [t]);
-        assertEquals(em.getTasksWithTag("B"), [t]);
-        assertEquals(em.getTasksWithAnyTag(["A"]), [t]);
-        assertEquals(em.getTasksWithAnyTag(["B"]), [t]);
-        assertEquals(em.getTasksWithAnyTag(["A","B"]), [t]);
-        assertEquals(em.getTasksWithAllTags(["A","B"]), [t]);
-        assertEquals(em.getTasksWithAllTags(["A"]), [t]);
-        assertEquals(em.getTasksWithAllTags(["B"]), [t]);
-    }
-
-    // ENGR-1796: if nothing matched first tag, then returned whatever matched second tag!
-    @Test
-    public void testRetrievingTasksWithAllTagsWhenFirstNotMatched() {
-        Task t = new BasicTask({ /*no-op*/ })
-        em.submit tags:["A"], t
-        t.get();
-
-        assertEquals(em.getTasksWithAllTags(["not_there","A"]), []);
-    }
-    
-    @Test
-    public void testRetrievedTasksIncludesTasksInProgress() {
-        CountDownLatch runningLatch = new CountDownLatch(1);
-        CountDownLatch finishLatch = new CountDownLatch(1);
-        Task t = new BasicTask({ runningLatch.countDown(); finishLatch.await() })
-        em.submit tags:["A"], t
-        
-        try {
-            runningLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
-    
-            assertEquals(em.getTasksWithTag("A"), [t]);
-        } finally {
-            finishLatch.countDown();
-        }
-    }
-    
-    @Test
-    public void cancelBeforeRun() {
-        CountDownLatch blockForever = new CountDownLatch(1);
-        
-        BasicTask t = [ { blockForever.await(); return 42 } ]
-        t.cancel true
-        assertTrue(t.isCancelled())
-        assertTrue(t.isDone())
-        assertTrue(t.isError())
-        em.submit tag:"A", t
-        try { t.get(); fail("get should have failed due to cancel"); } catch (CancellationException e) {}
-        assertTrue(t.isCancelled())
-        assertTrue(t.isDone())
-        assertTrue(t.isError())
-        
-        log.debug "cancelBeforeRun status: {}", t.getStatusDetail(false)
-        assertTrue(t.getStatusDetail(false).toLowerCase().contains("cancel"))
-    }
-
-    @Test
-    public void cancelDuringRun() {
-        CountDownLatch signalStarted = new CountDownLatch(1);
-        CountDownLatch blockForever = new CountDownLatch(1);
-        
-        BasicTask t = [ { synchronized (data) { signalStarted.countDown(); blockForever.await() }; return 42 } ]
-        em.submit tag:"A", t
-        assertFalse(t.isCancelled())
-        assertFalse(t.isDone())
-        assertFalse(t.isError())
-        
-        assertTrue(signalStarted.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        t.cancel true
-        
-        assertTrue(t.isCancelled())
-        assertTrue(t.isError())
-        try { t.get(); fail("get should have failed due to cancel"); } catch (CancellationException e) {}
-        assertTrue(t.isCancelled())
-        assertTrue(t.isDone())
-        assertTrue(t.isError())
-    }
-    
-    @Test
-    public void cancelAfterRun() {
-        BasicTask t = [ { return 42 } ]
-        em.submit tag:"A", t
-
-        assertEquals(42, t.get());
-        t.cancel true
-        assertFalse(t.isCancelled())
-        assertFalse(t.isError())
-        assertTrue(t.isDone())
-    }
-    
-    @Test
-    public void errorDuringRun() {
-        BasicTask t = [ { throw new IllegalStateException("Aaargh"); } ]
-        
-        em.submit tag:"A", t
-        
-        try { t.get(); fail("get should have failed due to error"); } catch (Exception eo) { Exception e = Throwables.getRootCause(eo); assertEquals("Aaargh", e.getMessage()) }
-        
-        assertFalse(t.isCancelled())
-        assertTrue(t.isError())
-        assertTrue(t.isDone())
-        
-        log.debug "errorDuringRun status: {}", t.getStatusDetail(false)
-        assertTrue(t.getStatusDetail(false).contains("Aaargh"), "details="+t.getStatusDetail(false))
-    }
-
-    @Test
-    public void fieldsSetForSimpleBasicTask() {
-        CountDownLatch signalStarted = new CountDownLatch(1);
-        CountDownLatch allowCompletion = new CountDownLatch(1);
-        
-        BasicTask t = [ { signalStarted.countDown(); allowCompletion.await(); return 42 } ]
-        assertEquals(null, t.submittedByTask)
-        assertEquals(-1, t.submitTimeUtc)
-        assertNull(t.getResult())
-
-        em.submit tag:"A", t
-        assertTrue(signalStarted.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        
-        assertTrue(t.submitTimeUtc > 0)
-        assertTrue(t.startTimeUtc >= t.submitTimeUtc)
-        assertNotNull(t.getResult())
-        assertEquals(-1, t.endTimeUtc)
-        assertEquals(false, t.isCancelled())
-        
-        allowCompletion.countDown()
-        assertEquals(42, t.get())
-        assertTrue(t.endTimeUtc >= t.startTimeUtc)
-
-        log.debug "BasicTask duration (millis): {}", (t.endTimeUtc - t.submitTimeUtc)
-    }
-
-    @Test
-    public void fieldsSetForBasicTaskSubmittedBasicTask() {
-        //submitted BasicTask B is started by A, and waits for A to complete
-        BasicTask t = new BasicTask( displayName: "sample", description: "some descr", {
-                em.submit tag:"B", {
-                assertEquals(45, em.getTasksWithTag("A").iterator().next().get());
-                46 };
-            45 } )
-        em.submit tag:"A", t
-
-        t.blockUntilEnded()
- 
-//        assertEquals em.getAllTasks().size(), 2
-        
-        BasicTask tb = em.getTasksWithTag("B").iterator().next();
-        assertEquals( 46, tb.get() )
-        assertEquals( t, em.getTasksWithTag("A").iterator().next() )
-        assertNull( t.submittedByTask )
-        
-        BasicTask submitter = tb.submittedByTask;
-        assertNotNull(submitter)
-        assertEquals("sample", submitter.displayName)
-        assertEquals("some descr", submitter.description)
-        assertEquals(t, submitter)
-        
-        assertTrue(submitter.submitTimeUtc <= tb.submitTimeUtc)
-        assertTrue(submitter.endTimeUtc <= tb.endTimeUtc)
-        
-        log.debug "BasicTask {} was submitted by {}", tb, submitter
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35363d61/core/src/test/java/brooklyn/util/task/BasicTaskExecutionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/BasicTaskExecutionTest.java b/core/src/test/java/brooklyn/util/task/BasicTaskExecutionTest.java
new file mode 100644
index 0000000..cd0d2b1
--- /dev/null
+++ b/core/src/test/java/brooklyn/util/task/BasicTaskExecutionTest.java
@@ -0,0 +1,460 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.util.task;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.management.Task;
+import brooklyn.test.Asserts;
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Callables;
+
+/**
+ * Test the operation of the {@link BasicTask} class.
+ *
+ * TODO clarify test purpose
+ */
+public class BasicTaskExecutionTest {
+    private static final Logger log = LoggerFactory.getLogger(BasicTaskExecutionTest.class);
+ 
+    private static final int TIMEOUT_MS = 10*1000;
+    
+    private BasicExecutionManager em;
+    private Map<Object, Object> data;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() {
+        em = new BasicExecutionManager("mycontext");
+        data = Collections.synchronizedMap(new HashMap<Object, Object>());
+        data.clear();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (em != null) em.shutdownNow();
+        if (data != null) data.clear();
+    }
+    
+    @Test
+    public void runSimpleBasicTask() throws Exception {
+        BasicTask<Object> t = new BasicTask<Object>(newPutCallable(1, "b"));
+        data.put(1, "a");
+        Task<Object> t2 = em.submit(MutableMap.of("tag", "A"), t);
+        assertEquals("a", t.get());
+        assertEquals("a", t2.get());
+        assertEquals("b", data.get(1));
+    }
+    
+    @Test
+    public void runSimpleRunnable() throws Exception {
+        data.put(1, "a");
+        Task<?> t = em.submit(MutableMap.of("tag", "A"), newPutRunnable(1, "b"));
+        assertEquals(null, t.get());
+        assertEquals("b", data.get(1));
+    }
+
+    @Test
+    public void runSimpleCallable() throws Exception {
+        data.put(1, "a");
+        Task<?> t = em.submit(MutableMap.of("tag", "A"), newPutCallable(1, "b"));
+        assertEquals("a", t.get());
+        assertEquals("b", data.get(1));
+    }
+
+    @Test
+    public void runBasicTaskWithWaits() throws Exception {
+        final CountDownLatch signalStarted = new CountDownLatch(1);
+        final CountDownLatch allowCompletion = new CountDownLatch(1);
+        final BasicTask<Object> t = new BasicTask<Object>(new Callable<Object>() {
+            public Object call() throws Exception {
+                Object result = data.put(1, "b");
+                signalStarted.countDown();
+                assertTrue(allowCompletion.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+                return result;
+            }});
+        data.put(1, "a");
+
+        Task<?> t2 = em.submit(MutableMap.of("tag", "A"), t);
+        assertEquals(t, t2);
+        assertFalse(t.isDone());
+        
+        assertTrue(signalStarted.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals("b", data.get(1));
+        assertFalse(t.isDone());
+        
+        log.debug("runBasicTaskWithWaits, BasicTask status: {}", t.getStatusDetail(false));
+        
+        Asserts.succeedsEventually(new Runnable() {
+            public void run() {
+                String status = t.getStatusDetail(false);
+                assertTrue(status != null && status.toLowerCase().contains("waiting"), "status="+status);
+            }});
+        
+        allowCompletion.countDown();
+        assertEquals("a", t.get());
+    }
+
+    @Test
+    public void runMultipleBasicTasks() throws Exception {
+        data.put(1, 1);
+        BasicExecutionManager em = new BasicExecutionManager("mycontext");
+        for (int i = 0; i < 2; i++) {
+            em.submit(MutableMap.of("tag", "A"), new BasicTask<Integer>(newIncrementCallable(1)));
+            em.submit(MutableMap.of("tag", "B"), new BasicTask<Integer>(newIncrementCallable((1))));
+        }
+        int total = 0;
+        for (Object tag : em.getTaskTags()) {
+                log.debug("tag {}", tag);
+                for (Task<?> task : em.getTasksWithTag(tag)) {
+                    log.debug("BasicTask {}, has {}", task, task.get());
+                    total += (Integer)task.get();
+                }
+            }
+        assertEquals(10, total);
+        //now that all have completed:
+        assertEquals(5, data.get(1));
+    }
+
+    @Test
+    public void runMultipleBasicTasksMultipleTags() throws Exception {
+        data.put(1, 1);
+        Collection<Task<Integer>> tasks = Lists.newArrayList();
+        tasks.add(em.submit(MutableMap.of("tag", "A"), new BasicTask<Integer>(newIncrementCallable(1))));
+        tasks.add(em.submit(MutableMap.of("tags", ImmutableList.of("A","B")), new BasicTask<Integer>(newIncrementCallable(1))));
+        tasks.add(em.submit(MutableMap.of("tags", ImmutableList.of("B","C")), new BasicTask<Integer>(newIncrementCallable(1))));
+        tasks.add(em.submit(MutableMap.of("tags", ImmutableList.of("D")), new BasicTask<Integer>(newIncrementCallable(1))));
+        int total = 0;
+
+        for (Task<Integer> t : tasks) {
+            log.debug("BasicTask {}, has {}", t, t.get());
+            total += t.get();
+            }
+        assertEquals(10, total);
+ 
+        //now that all have completed:
+        assertEquals(data.get(1), 5);
+        assertEquals(em.getTasksWithTag("A").size(), 2);
+        assertEquals(em.getTasksWithAnyTag(ImmutableList.of("A")).size(), 2);
+        assertEquals(em.getTasksWithAllTags(ImmutableList.of("A")).size(), 2);
+
+        assertEquals(em.getTasksWithAnyTag(ImmutableList.of("A", "B")).size(), 3);
+        assertEquals(em.getTasksWithAllTags(ImmutableList.of("A", "B")).size(), 1);
+        assertEquals(em.getTasksWithAllTags(ImmutableList.of("B", "C")).size(), 1);
+        assertEquals(em.getTasksWithAnyTag(ImmutableList.of("A", "D")).size(), 3);
+    }
+
+    @Test
+    public void testGetTaskById() throws Exception {
+        Task<?> t = new BasicTask<Void>(newNoop());
+        em.submit(MutableMap.of("tag", "A"), t);
+        assertEquals(em.getTask(t.getId()), t);
+    }
+
+    @Test
+    public void testRetrievingTasksWithTagsReturnsExpectedTask() throws Exception {
+        Task<?> t = new BasicTask<Void>(newNoop());
+        em.submit(MutableMap.of("tag", "A"), t);
+        t.get();
+
+        assertEquals(em.getTasksWithTag("A"), ImmutableList.of(t));
+        assertEquals(em.getTasksWithAnyTag(ImmutableList.of("A")), ImmutableList.of(t));
+        assertEquals(em.getTasksWithAnyTag(ImmutableList.of("A", "B")), ImmutableList.of(t));
+        assertEquals(em.getTasksWithAllTags(ImmutableList.of("A")), ImmutableList.of(t));
+    }
+
+    @Test
+    public void testRetrievingTasksWithTagsExcludesNonMatchingTasks() throws Exception {
+        Task<?> t = new BasicTask<Void>(newNoop());
+        em.submit(MutableMap.of("tag", "A"), t);
+        t.get();
+
+        assertEquals(em.getTasksWithTag("B"), ImmutableSet.of());
+        assertEquals(em.getTasksWithAnyTag(ImmutableList.of("B")), ImmutableSet.of());
+        assertEquals(em.getTasksWithAllTags(ImmutableList.of("A", "B")), ImmutableSet.of());
+    }
+    
+    @Test
+    public void testRetrievingTasksWithMultipleTags() throws Exception {
+        Task<?> t = new BasicTask<Void>(newNoop());
+        em.submit(MutableMap.of("tags", ImmutableList.of("A", "B")), t);
+        t.get();
+
+        assertEquals(em.getTasksWithTag("A"), ImmutableList.of(t));
+        assertEquals(em.getTasksWithTag("B"), ImmutableList.of(t));
+        assertEquals(em.getTasksWithAnyTag(ImmutableList.of("A")), ImmutableList.of(t));
+        assertEquals(em.getTasksWithAnyTag(ImmutableList.of("B")), ImmutableList.of(t));
+        assertEquals(em.getTasksWithAnyTag(ImmutableList.of("A", "B")), ImmutableList.of(t));
+        assertEquals(em.getTasksWithAllTags(ImmutableList.of("A", "B")), ImmutableList.of(t));
+        assertEquals(em.getTasksWithAllTags(ImmutableList.of("A")), ImmutableList.of(t));
+        assertEquals(em.getTasksWithAllTags(ImmutableList.of("B")), ImmutableList.of(t));
+    }
+
+    // ENGR-1796: if nothing matched first tag, then returned whatever matched second tag!
+    @Test
+    public void testRetrievingTasksWithAllTagsWhenFirstNotMatched() throws Exception {
+        Task<?> t = new BasicTask<Void>(newNoop());
+        em.submit(MutableMap.of("tags", ImmutableList.of("A")), t);
+        t.get();
+
+        assertEquals(em.getTasksWithAllTags(ImmutableList.of("not_there","A")), ImmutableSet.of());
+    }
+    
+    @Test
+    public void testRetrievedTasksIncludesTasksInProgress() throws Exception {
+        final CountDownLatch runningLatch = new CountDownLatch(1);
+        final CountDownLatch finishLatch = new CountDownLatch(1);
+        Task<Void> t = new BasicTask<Void>(new Callable<Void>() {
+            public Void call() throws Exception {
+                runningLatch.countDown();
+                finishLatch.await();
+                return null;
+            }});
+        em.submit(MutableMap.of("tags", ImmutableList.of("A")), t);
+        
+        try {
+            runningLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+    
+            assertEquals(em.getTasksWithTag("A"), ImmutableList.of(t));
+        } finally {
+            finishLatch.countDown();
+        }
+    }
+    
+    @Test
+    public void cancelBeforeRun() throws Exception {
+        final CountDownLatch blockForever = new CountDownLatch(1);
+        
+        BasicTask<Integer> t = new BasicTask<Integer>(new Callable<Integer>() {
+            public Integer call() throws Exception {
+                blockForever.await(); return 42;
+            }});
+        t.cancel(true);
+        assertTrue(t.isCancelled());
+        assertTrue(t.isDone());
+        assertTrue(t.isError());
+        em.submit(MutableMap.of("tag", "A"), t);
+        try {
+            t.get();
+            fail("get should have failed due to cancel");
+        } catch (CancellationException e) {
+            // expected
+        }
+        assertTrue(t.isCancelled());
+        assertTrue(t.isDone());
+        assertTrue(t.isError());
+        
+        log.debug("cancelBeforeRun status: {}", t.getStatusDetail(false));
+        assertTrue(t.getStatusDetail(false).toLowerCase().contains("cancel"));
+    }
+
+    @Test
+    public void cancelDuringRun() throws Exception {
+        final CountDownLatch signalStarted = new CountDownLatch(1);
+        final CountDownLatch blockForever = new CountDownLatch(1);
+        
+        BasicTask<Integer> t = new BasicTask<Integer>(new Callable<Integer>() {
+            public Integer call() throws Exception {
+                synchronized (data) {
+                    signalStarted.countDown();
+                    blockForever.await();
+                }
+                return 42;
+            }});
+        em.submit(MutableMap.of("tag", "A"), t);
+        assertFalse(t.isCancelled());
+        assertFalse(t.isDone());
+        assertFalse(t.isError());
+        
+        assertTrue(signalStarted.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        t.cancel(true);
+        
+        assertTrue(t.isCancelled());
+        assertTrue(t.isError());
+        try {
+            t.get();
+            fail("get should have failed due to cancel");
+        } catch (CancellationException e) {
+            // expected
+        }
+        assertTrue(t.isCancelled());
+        assertTrue(t.isDone());
+        assertTrue(t.isError());
+    }
+    
+    @Test
+    public void cancelAfterRun() throws Exception {
+        BasicTask<Integer> t = new BasicTask<Integer>(Callables.returning(42));
+        em.submit(MutableMap.of("tag", "A"), t);
+
+        assertEquals(t.get(), (Integer)42);
+        t.cancel(true);
+        assertFalse(t.isCancelled());
+        assertFalse(t.isError());
+        assertTrue(t.isDone());
+    }
+    
+    @Test
+    public void errorDuringRun() throws Exception {
+        BasicTask<Void> t = new BasicTask<Void>(new Callable<Void>() {
+            public Void call() throws Exception {
+                throw new IllegalStateException("Simulating failure in errorDuringRun");
+            }});
+        
+        em.submit(MutableMap.of("tag", "A"), t);
+        
+        try {
+            t.get();
+            fail("get should have failed due to error"); 
+        } catch (Exception eo) { 
+            Throwable e = Throwables.getRootCause(eo);
+            assertEquals("Simulating failure in errorDuringRun", e.getMessage());
+        }
+        
+        assertFalse(t.isCancelled());
+        assertTrue(t.isError());
+        assertTrue(t.isDone());
+        
+        log.debug("errorDuringRun status: {}", t.getStatusDetail(false));
+        assertTrue(t.getStatusDetail(false).contains("Simulating failure in errorDuringRun"), "details="+t.getStatusDetail(false));
+    }
+
+    @Test
+    public void fieldsSetForSimpleBasicTask() throws Exception {
+        final CountDownLatch signalStarted = new CountDownLatch(1);
+        final CountDownLatch allowCompletion = new CountDownLatch(1);
+        
+        BasicTask<Integer> t = new BasicTask<Integer>(new Callable<Integer>() {
+            public Integer call() throws Exception {
+                signalStarted.countDown();
+                allowCompletion.await();
+                return 42;
+            }});
+        assertEquals(null, t.submittedByTask);
+        assertEquals(-1, t.submitTimeUtc);
+        assertNull(t.getResult());
+
+        em.submit(MutableMap.of("tag", "A"), t);
+        assertTrue(signalStarted.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        
+        assertTrue(t.submitTimeUtc > 0);
+        assertTrue(t.startTimeUtc >= t.submitTimeUtc);
+        assertNotNull(t.getResult());
+        assertEquals(-1, t.endTimeUtc);
+        assertEquals(false, t.isCancelled());
+        
+        allowCompletion.countDown();
+        assertEquals(t.get(), (Integer)42);
+        assertTrue(t.endTimeUtc >= t.startTimeUtc);
+
+        log.debug("BasicTask duration (millis): {}", (t.endTimeUtc - t.submitTimeUtc));
+    }
+
+    @Test
+    public void fieldsSetForBasicTaskSubmittedBasicTask() throws Exception {
+        //submitted BasicTask B is started by A, and waits for A to complete
+        BasicTask<Integer> t = new BasicTask<Integer>(MutableMap.of("displayName", "sample", "description", "some descr"), new Callable<Integer>() {
+            public Integer call() throws Exception {
+                em.submit(MutableMap.of("tag", "B"), new Callable<Integer>() {
+                    public Integer call() throws Exception {
+                        assertEquals(45, em.getTasksWithTag("A").iterator().next().get());
+                        return 46;
+                    }});
+                return 45;
+            }});
+        em.submit(MutableMap.of("tag", "A"), t);
+
+        t.blockUntilEnded();
+ 
+//        assertEquals(em.getAllTasks().size(), 2
+        
+        BasicTask<?> tb = (BasicTask<?>) em.getTasksWithTag("B").iterator().next();
+        assertEquals( 46, tb.get() );
+        assertEquals( t, em.getTasksWithTag("A").iterator().next() );
+        assertNull( t.submittedByTask );
+        
+        BasicTask<?> submitter = (BasicTask<?>) tb.submittedByTask;
+        assertNotNull(submitter);
+        assertEquals("sample", submitter.displayName);
+        assertEquals("some descr", submitter.description);
+        assertEquals(t, submitter);
+        
+        assertTrue(submitter.submitTimeUtc <= tb.submitTimeUtc);
+        assertTrue(submitter.endTimeUtc <= tb.endTimeUtc);
+        
+        log.debug("BasicTask {} was submitted by {}", tb, submitter);
+    }
+    
+    private Callable<Object> newPutCallable(final Object key, final Object val) {
+        return new Callable<Object>() {
+            public Object call() {
+                return data.put(key, val);
+            }
+        };
+    }
+    
+    private Callable<Integer> newIncrementCallable(final Object key) {
+        return new Callable<Integer>() {
+            public Integer call() {
+                synchronized (data) {
+                    return (Integer) data.put(key, (Integer)data.get(key) + 1);
+                }
+            }
+        };
+    }
+    
+    private Runnable newPutRunnable(final Object key, final Object val) {
+        return new Runnable() {
+            public void run() {
+                data.put(key, val);
+            }
+        };
+    }
+    
+    private Runnable newNoop() {
+        return new Runnable() {
+            public void run() {
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35363d61/core/src/test/java/brooklyn/util/task/ScheduledExecutionTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/ScheduledExecutionTest.groovy b/core/src/test/java/brooklyn/util/task/ScheduledExecutionTest.groovy
deleted file mode 100644
index 1e4731d..0000000
--- a/core/src/test/java/brooklyn/util/task/ScheduledExecutionTest.groovy
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task
-
-import static org.testng.Assert.*
-
-import java.util.concurrent.CopyOnWriteArrayList
-import java.util.concurrent.TimeUnit
-
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.testng.annotations.Test
-
-import brooklyn.test.TestUtils
-import brooklyn.util.internal.TimeExtras
-
-import com.google.common.collect.Lists
-
-public class ScheduledExecutionTest {
-
-	public static final Logger log = LoggerFactory.getLogger(ScheduledExecutionTest.class);
-	
-	static { TimeExtras.init() }
-	
-	@Test
-	public void testScheduledTask() {
-		int PERIOD = 20;
-		BasicExecutionManager m = new BasicExecutionManager();
-		int i=0;
-		def t = new ScheduledTask(delay: 2*PERIOD*TimeUnit.MILLISECONDS, period: PERIOD*TimeUnit.MILLISECONDS, maxIterations: 5, { new BasicTask({
-			log.debug "task running: "+Tasks.current()+" "+Tasks.current().getStatusDetail(false)
-			++i; 
-		}) } );
-	
-		log.info "submitting {} {}", t, t.getStatusDetail(false)
-		m.submit(t);
-		log.info "submitted {} {}", t, t.getStatusDetail(false)
-		int interimResult = t.get()
-		log.info "done one ({}) {} {}", interimResult, t, t.getStatusDetail(false)
-		assertTrue(i>0)
-		t.blockUntilEnded()
-		int finalResult = t.get()
-		log.info "ended ({}) {} {}", finalResult, t, t.getStatusDetail(false)
-		assertEquals(finalResult, 5)
-		assertEquals(i, 5)
-	}
-
-	/** like testScheduledTask but the loop is terminated by the task itself adjusting the period */
-	@Test
-	public void testScheduledTaskSelfEnding() {
-		int PERIOD = 20;
-		BasicExecutionManager m = new BasicExecutionManager();
-		int i=0;
-		def t = new ScheduledTask(delay: 2*PERIOD*TimeUnit.MILLISECONDS, period: PERIOD*TimeUnit.MILLISECONDS, { new BasicTask({
-			if (i>=4) Tasks.current().submittedByTask.period = null
-			log.info "task running (${i}): "+Tasks.current()+" "+Tasks.current().getStatusDetail(false)
-			++i;
-		}) } );
-	
-		log.info "submitting {} {}", t, t.getStatusDetail(false)
-		m.submit(t);
-		log.info "submitted {} {}", t, t.getStatusDetail(false)
-		int interimResult = t.get()
-		log.info "done one ({}) {} {}", interimResult, t, t.getStatusDetail(false)
-		assertTrue(i>0)
-		t.blockUntilEnded()
-		int finalResult = t.get()
-		log.info "ended ({}) {} {}", finalResult, t, t.getStatusDetail(false)
-		assertEquals(finalResult, 5)
-		assertEquals(i, 5)
-	}
-
-	@Test
-	public void testScheduledTaskCancelEnding() {
-		int PERIOD = 20;
-		BasicExecutionManager m = new BasicExecutionManager();
-		int i=0;
-		def t = new ScheduledTask(delay: 2*PERIOD*TimeUnit.MILLISECONDS, period: PERIOD*TimeUnit.MILLISECONDS, { new BasicTask({
-			log.info "task running (${i}): "+Tasks.current()+" "+Tasks.current().getStatusDetail(false)
-			++i;
-			if (i>=5) Tasks.current().submittedByTask.cancel()
-			i
-		}) } );
-	
-		log.info "submitting {} {}", t, t.getStatusDetail(false)
-		m.submit(t);
-		log.info "submitted {} {}", t, t.getStatusDetail(false)
-		int interimResult = t.get()
-		log.info "done one ({}) {} {}", interimResult, t, t.getStatusDetail(false)
-		assertTrue(i>0)
-		t.blockUntilEnded()
-//		int finalResult = t.get()
-		log.info "ended ({}) {} {}", i, t, t.getStatusDetail(false)
-//		assertEquals(finalResult, 5)
-		assertEquals(i, 5)
-	}
-
-    @Test(groups="Integration")
-    public void testScheduledTaskTakesLongerThanPeriod() {
-        final int PERIOD = 1;
-        final int SLEEP_TIME = 100;
-        final int EARLY_RETURN_GRACE = 10;
-        BasicExecutionManager m = new BasicExecutionManager();
-        final List<Long> execTimes = new CopyOnWriteArrayList<Long>();
-        
-        def t = new ScheduledTask(delay: PERIOD*TimeUnit.MILLISECONDS, period: PERIOD*TimeUnit.MILLISECONDS, { new BasicTask({
-            execTimes.add(System.currentTimeMillis());
-            Thread.sleep(100);
-        }) } );
-    
-        m.submit(t);
-        
-        TestUtils.executeUntilSucceeds {
-            execTimes.size() > 3;
-        }
-        
-        List<Long> timeDiffs = Lists.newArrayList();
-        long prevExecTime = -1;
-        for (Long execTime : execTimes) {
-            if (prevExecTime == -1) {
-                prevExecTime = execTime;
-            } else {
-                timeDiffs.add(execTime - prevExecTime);
-                prevExecTime = execTime;
-            }
-        }
-        
-        for (Long timeDiff : timeDiffs) {
-            if (timeDiff < (SLEEP_TIME - EARLY_RETURN_GRACE)) fail("timeDiffs="+timeDiffs+"; execTimes="+execTimes);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35363d61/core/src/test/java/brooklyn/util/task/ScheduledExecutionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/ScheduledExecutionTest.java b/core/src/test/java/brooklyn/util/task/ScheduledExecutionTest.java
new file mode 100644
index 0000000..9cda495
--- /dev/null
+++ b/core/src/test/java/brooklyn/util/task/ScheduledExecutionTest.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.util.task;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import brooklyn.management.Task;
+import brooklyn.test.Asserts;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+
+import com.google.common.collect.Lists;
+
+public class ScheduledExecutionTest {
+
+	public static final Logger log = LoggerFactory.getLogger(ScheduledExecutionTest.class);
+	
+	@Test
+	public void testScheduledTask() throws Exception {
+		int PERIOD = 20;
+		BasicExecutionManager m = new BasicExecutionManager("mycontextid");
+		final AtomicInteger i = new AtomicInteger(0);
+		ScheduledTask t = new ScheduledTask(MutableMap.of("delay", 2*PERIOD, "period", PERIOD, "maxIterations", 5), new Callable<Task<?>>() {
+            public Task<?> call() throws Exception {
+                return new BasicTask<Integer>(new Callable<Integer>() {
+                    public Integer call() {
+            			log.debug("task running: "+Tasks.current()+" "+Tasks.current().getStatusDetail(false));
+            			return i.incrementAndGet();
+                    }});
+            }});
+	
+		log.info("submitting {} {}", t, t.getStatusDetail(false));
+		m.submit(t);
+		log.info("submitted {} {}", t, t.getStatusDetail(false));
+		Integer interimResult = (Integer) t.get();
+		log.info("done one ({}) {} {}", new Object[] {interimResult, t, t.getStatusDetail(false)});
+		assertTrue(i.get() > 0, "i="+i);
+		t.blockUntilEnded();
+		Integer finalResult = (Integer) t.get();
+		log.info("ended ({}) {} {}", new Object[] {finalResult, t, t.getStatusDetail(false)});
+		assertEquals(finalResult, (Integer)5);
+		assertEquals(i.get(), 5);
+	}
+
+	/** like testScheduledTask but the loop is terminated by the task itself adjusting the period */
+	@Test
+	public void testScheduledTaskSelfEnding() throws Exception {
+		int PERIOD = 20;
+		BasicExecutionManager m = new BasicExecutionManager("mycontextid");
+		final AtomicInteger i = new AtomicInteger(0);
+		ScheduledTask t = new ScheduledTask(MutableMap.of("delay", 2*PERIOD, "period", PERIOD), new Callable<Task<?>>() {
+		    public Task<?> call() throws Exception {
+		        return new BasicTask<Integer>(new Callable<Integer>() {
+		            public Integer call() {
+		                ScheduledTask submitter = (ScheduledTask) ((BasicTask)Tasks.current()).submittedByTask;
+            			if (i.get() >= 4) submitter.period = null;
+            			log.info("task running ("+i+"): "+Tasks.current()+" "+Tasks.current().getStatusDetail(false));
+            			return i.incrementAndGet();
+		            }});
+		    }});
+	
+		log.info("submitting {} {}", t, t.getStatusDetail(false));
+		m.submit(t);
+		log.info("submitted {} {}", t, t.getStatusDetail(false));
+		Integer interimResult = (Integer) t.get();
+		log.info("done one ({}) {} {}", new Object[] {interimResult, t, t.getStatusDetail(false)});
+		assertTrue(i.get() > 0);
+		t.blockUntilEnded();
+		Integer finalResult = (Integer) t.get();
+		log.info("ended ({}) {} {}", new Object[] {finalResult, t, t.getStatusDetail(false)});
+		assertEquals(finalResult, (Integer)5);
+		assertEquals(i.get(), 5);
+	}
+
+	@Test
+	public void testScheduledTaskCancelEnding() throws Exception {
+		int PERIOD = 20;
+		BasicExecutionManager m = new BasicExecutionManager("mycontextid");
+		final AtomicInteger i = new AtomicInteger();
+		ScheduledTask t = new ScheduledTask(MutableMap.of("delay", 2*PERIOD, "period", PERIOD), new Callable<Task<?>>() {
+            public Task<?> call() throws Exception {
+                return new BasicTask<Integer>(new Callable<Integer>() {
+                    public Integer call() {
+            			log.info("task running ("+i+"): "+Tasks.current()+" "+Tasks.current().getStatusDetail(false));
+            			ScheduledTask submitter = (ScheduledTask) ((BasicTask)Tasks.current()).submittedByTask;
+            			i.incrementAndGet();
+            			if (i.get() >= 5) submitter.cancel();
+            			return i.get();
+                    }});
+            }});
+	
+		log.info("submitting {} {}", t, t.getStatusDetail(false));
+		m.submit(t);
+		log.info("submitted {} {}", t, t.getStatusDetail(false));
+		Integer interimResult = (Integer) t.get();
+		log.info("done one ({}) {} {}", new Object[] {interimResult, t, t.getStatusDetail(false)});
+		assertTrue(i.get() > 0);
+		t.blockUntilEnded();
+//		int finalResult = t.get()
+		log.info("ended ({}) {} {}", new Object[] {i, t, t.getStatusDetail(false)});
+//		assertEquals(finalResult, 5)
+		assertEquals(i.get(), 5);
+	}
+
+    @Test(groups="Integration")
+    public void testScheduledTaskTakesLongerThanPeriod() throws Exception {
+        final int PERIOD = 1;
+        final int SLEEP_TIME = 100;
+        final int EARLY_RETURN_GRACE = 10;
+        BasicExecutionManager m = new BasicExecutionManager("mycontextid");
+        final List<Long> execTimes = new CopyOnWriteArrayList<Long>();
+        
+        ScheduledTask t = new ScheduledTask(MutableMap.of("delay", PERIOD, "period", PERIOD), new Callable<Task<?>>() {
+            public Task<?> call() throws Exception {
+                return new BasicTask<Void>(new Runnable() {
+                    public void run() {
+                        execTimes.add(System.currentTimeMillis());
+                        try {
+                            Thread.sleep(100);
+                        } catch (InterruptedException e) {
+                            throw Exceptions.propagate(e);
+                        }
+                    }});
+            }});
+    
+        m.submit(t);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            public void run() {
+                assertTrue(execTimes.size() > 3, "size="+execTimes.size());
+            }});
+        
+        List<Long> timeDiffs = Lists.newArrayList();
+        long prevExecTime = -1;
+        for (Long execTime : execTimes) {
+            if (prevExecTime == -1) {
+                prevExecTime = execTime;
+            } else {
+                timeDiffs.add(execTime - prevExecTime);
+                prevExecTime = execTime;
+            }
+        }
+        
+        for (Long timeDiff : timeDiffs) {
+            if (timeDiff < (SLEEP_TIME - EARLY_RETURN_GRACE)) fail("timeDiffs="+timeDiffs+"; execTimes="+execTimes);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35363d61/core/src/test/java/brooklyn/util/task/SingleThreadedSchedulerTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/SingleThreadedSchedulerTest.groovy b/core/src/test/java/brooklyn/util/task/SingleThreadedSchedulerTest.groovy
deleted file mode 100644
index debc8e6..0000000
--- a/core/src/test/java/brooklyn/util/task/SingleThreadedSchedulerTest.groovy
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task
-
-import static brooklyn.test.TestUtils.*
-import static org.testng.Assert.*
-
-import java.util.concurrent.CancellationException
-import java.util.concurrent.CopyOnWriteArrayList
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.Future
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.TimeoutException
-import java.util.concurrent.atomic.AtomicInteger
-
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.testng.annotations.AfterMethod
-import org.testng.annotations.BeforeMethod
-import org.testng.annotations.Test
-
-public class SingleThreadedSchedulerTest {
-
-    private static final Logger log = LoggerFactory.getLogger(SingleThreadedSchedulerTest)
-    
-    private BasicExecutionManager em
-    
-    @BeforeMethod
-    public void setUp() {
-        em = new BasicExecutionManager()
-        em.setTaskSchedulerForTag("category1", SingleThreadedScheduler.class);
-    }
-    
-    @AfterMethod
-    public void tearDown() {
-        em?.shutdownNow()
-    }
-    
-    @Test
-    public void testExecutesInOrder() {
-        final int NUM_TIMES = 1000
-        final List<Integer> result = new CopyOnWriteArrayList()
-        for (i in 0..(NUM_TIMES-1)) {
-            final counter = i
-            em.submit(tag:"category1", { result.add(counter) })
-        }
-        
-        executeUntilSucceeds {
-            assertEquals(result.size(), NUM_TIMES)
-        }
-
-        for (i in 0..(NUM_TIMES-1)) {
-            assertEquals(result.get(i), i)
-        }        
-    }
-    
-    @Test
-    public void testLargeQueueDoesNotConsumeTooManyThreads() {
-        final int NUM_TIMES = 3000
-        final CountDownLatch latch = new CountDownLatch(1)
-        BasicTask blockingTask = [ { latch.await() } ]
-        em.submit tag:"category1", blockingTask
-        
-        final AtomicInteger counter = new AtomicInteger(0)
-        for (i in 1..NUM_TIMES) {
-            BasicTask t = [ {counter.incrementAndGet()} ]
-            em.submit tag:"category1", t
-            if (i % 500 == 0) log.info("Submitted $i jobs...")
-        }
-
-        Thread.sleep(100) // give it more of a chance to create the threads before we let them execute
-        latch.countDown()
-
-        executeUntilSucceeds {
-            assertEquals(counter.get(), NUM_TIMES)
-        }
-    }
-    
-    @Test
-    public void testGetResultOfQueuedTaskBeforeItExecutes() {
-        final CountDownLatch latch = new CountDownLatch(1)
-        em.submit([tag:"category1"], { latch.await() })
-        
-        BasicTask t = [ {return 123} ]
-        Future future = em.submit tag:"category1", t
-
-        new Thread({Thread.sleep(10);latch.countDown()}).start();
-        assertEquals(future.get(), 123)
-    }
-    
-    @Test
-    public void testGetResultOfQueuedTaskBeforeItExecutesWithTimeout() {
-        final CountDownLatch latch = new CountDownLatch(1)
-        em.submit([tag:"category1"], { latch.await() })
-        
-        BasicTask t = [ {return 123} ]
-        Future future = em.submit tag:"category1", t
-
-        try {
-            assertEquals(future.get(10, TimeUnit.MILLISECONDS), 123)
-            fail()
-        } catch (TimeoutException e) {
-            // success
-        }
-    }
-    
-    @Test
-    public void testCancelQueuedTaskBeforeItExecutes() {
-        final CountDownLatch latch = new CountDownLatch(1)
-        em.submit([tag:"category1"], { latch.await() })
-        
-        boolean executed = false
-        BasicTask t = [ {execututed = true} ]
-        Future future = em.submit tag:"category1", t
-
-        future.cancel(true)
-        latch.countDown()
-        Thread.sleep(10)
-        try {
-            future.get()
-        } catch (CancellationException e) {
-            // success
-        }
-        assertFalse(executed)
-    }
-    
-    @Test
-    public void testGetResultOfQueuedTaskAfterItExecutes() {
-        final CountDownLatch latch = new CountDownLatch(1)
-        em.submit([tag:"category1"], { latch.await() })
-        
-        BasicTask t = [ {return 123} ]
-        Future future = em.submit tag:"category1", t
-
-        latch.countDown()
-        assertEquals(future.get(), 123)
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35363d61/core/src/test/java/brooklyn/util/task/SingleThreadedSchedulerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/SingleThreadedSchedulerTest.java b/core/src/test/java/brooklyn/util/task/SingleThreadedSchedulerTest.java
new file mode 100644
index 0000000..265956d
--- /dev/null
+++ b/core/src/test/java/brooklyn/util/task/SingleThreadedSchedulerTest.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.util.task;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.fail;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.test.Asserts;
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.util.concurrent.Callables;
+
+public class SingleThreadedSchedulerTest {
+
+    private static final Logger log = LoggerFactory.getLogger(SingleThreadedSchedulerTest.class);
+    
+    private BasicExecutionManager em;
+    
+    @BeforeMethod
+    public void setUp() {
+        em = new BasicExecutionManager("mycontextid");
+        em.setTaskSchedulerForTag("category1", SingleThreadedScheduler.class);
+    }
+    
+    @AfterMethod
+    public void tearDown() {
+        if (em != null) em.shutdownNow();
+    }
+    
+    @Test
+    public void testExecutesInOrder() throws Exception {
+        final int NUM_TIMES = 1000;
+        final List<Integer> result = new CopyOnWriteArrayList<Integer>();
+        for (int i = 0; i < NUM_TIMES; i++) {
+            final int counter = i;
+            em.submit(MutableMap.of("tag", "category1"), new Runnable() {
+                public void run() {
+                    result.add(counter);
+                }});
+        }
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(result.size(), NUM_TIMES);
+            }});
+
+        for (int i = 0; i < NUM_TIMES; i++) {
+            assertEquals(result.get(i), (Integer)i);
+        }        
+    }
+    
+    @Test
+    public void testLargeQueueDoesNotConsumeTooManyThreads() throws Exception {
+        final int NUM_TIMES = 3000;
+        final CountDownLatch latch = new CountDownLatch(1);
+        BasicTask<Void> blockingTask = new BasicTask<Void>(newLatchAwaiter(latch));
+        em.submit(MutableMap.of("tag", "category1"), blockingTask);
+        
+        final AtomicInteger counter = new AtomicInteger(0);
+        for (int i = 0; i < NUM_TIMES; i++) {
+            BasicTask<Void> t = new BasicTask<Void>(new Runnable() {
+                public void run() {
+                    counter.incrementAndGet();
+                }});
+            em.submit(MutableMap.of("tag", "category1"), t);
+            if (i % 500 == 0) log.info("Submitted "+i+" jobs...");
+        }
+
+        Thread.sleep(100); // give it more of a chance to create the threads before we let them execute
+        latch.countDown();
+
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(counter.get(), NUM_TIMES);
+            }});
+    }
+    
+    @Test
+    public void testGetResultOfQueuedTaskBeforeItExecutes() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        em.submit(MutableMap.of("tag", "category1"), newLatchAwaiter(latch));
+        
+        BasicTask<Integer> t = new BasicTask<Integer>(Callables.returning(123));
+        Future<Integer> future = em.submit(MutableMap.of("tag", "category1"), t);
+
+        Thread thread = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    Thread.sleep(10);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+                latch.countDown();
+            }});
+        thread.start();
+        assertEquals(future.get(), (Integer)123);
+    }
+    
+    @Test
+    public void testGetResultOfQueuedTaskBeforeItExecutesWithTimeout() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        em.submit(MutableMap.of("tag", "category1"), newLatchAwaiter(latch));
+        
+        BasicTask<Integer> t = new BasicTask<Integer>(Callables.returning(123));
+        Future<Integer> future = em.submit(MutableMap.of("tag", "category1"), t);
+
+        try {
+            assertEquals(future.get(10, TimeUnit.MILLISECONDS), (Integer)123);
+            fail();
+        } catch (TimeoutException e) {
+            // success
+        }
+    }
+    
+    @Test
+    public void testCancelQueuedTaskBeforeItExecutes() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        em.submit(MutableMap.of("tag", "category1"), newLatchAwaiter(latch));
+        
+        final AtomicBoolean executed = new AtomicBoolean();
+        BasicTask<?> t = new BasicTask<Void>(new Runnable() {
+            public void run() {
+                executed.set(true);
+            }});
+        Future<?> future = em.submit(MutableMap.of("tag", "category1"), t);
+
+        future.cancel(true);
+        latch.countDown();
+        Thread.sleep(10);
+        try {
+            future.get();
+        } catch (CancellationException e) {
+            // success
+        }
+        assertFalse(executed.get());
+    }
+    
+    @Test
+    public void testGetResultOfQueuedTaskAfterItExecutes() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        em.submit(MutableMap.of("tag", "category1"), newLatchAwaiter(latch));
+        
+        BasicTask<Integer> t = new BasicTask<Integer>(Callables.returning(123));
+        Future<Integer> future = em.submit(MutableMap.of("tag", "category1"), t);
+
+        latch.countDown();
+        assertEquals(future.get(), (Integer)123);
+    }
+    
+    private Callable<Void> newLatchAwaiter(final CountDownLatch latch) {
+        return new Callable<Void>() {
+            public Void call() throws Exception {
+                latch.await();
+                return null;
+            }
+        };
+    }
+}


[07/13] git commit: add an initializer for adding tags, with a test

Posted by he...@apache.org.
add an initializer for adding tags, with a test


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

Branch: refs/heads/master
Commit: 1a294f76a1e955582c5dc4d7c99526b60afef421
Parents: 78da216
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Jul 18 12:25:48 2014 -0400
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Jul 18 12:26:20 2014 -0400

----------------------------------------------------------------------
 .../entity/basic/EntityInitializers.java        | 30 ++++++++++++++++++++
 .../brooklyn/entity/basic/EntitiesTest.java     |  6 ++--
 .../test/java/brooklyn/util/net/UrlsTest.java   |  5 ++++
 3 files changed, 39 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1a294f76/core/src/main/java/brooklyn/entity/basic/EntityInitializers.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/EntityInitializers.java b/core/src/main/java/brooklyn/entity/basic/EntityInitializers.java
new file mode 100644
index 0000000..62db62e
--- /dev/null
+++ b/core/src/main/java/brooklyn/entity/basic/EntityInitializers.java
@@ -0,0 +1,30 @@
+package brooklyn.entity.basic;
+
+import java.util.List;
+
+import brooklyn.entity.proxying.EntityInitializer;
+
+import com.google.common.collect.ImmutableList;
+
+public class EntityInitializers {
+
+    public static class AddTags implements EntityInitializer {
+        public final List<Object> tags;
+        
+        public AddTags(Object... tags) {
+            this.tags = ImmutableList.copyOf(tags);
+        }
+        
+        @Override
+        public void apply(EntityLocal entity) {
+            for (Object tag: tags)
+                entity.addTag(tag);
+        }
+    }
+
+    
+    public static EntityInitializer addingTags(Object... tags) {
+        return new AddTags(tags);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1a294f76/core/src/test/java/brooklyn/entity/basic/EntitiesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/EntitiesTest.java b/core/src/test/java/brooklyn/entity/basic/EntitiesTest.java
index 3f72388..8acdf72 100644
--- a/core/src/test/java/brooklyn/entity/basic/EntitiesTest.java
+++ b/core/src/test/java/brooklyn/entity/basic/EntitiesTest.java
@@ -106,7 +106,9 @@ public class EntitiesTest extends BrooklynAppUnitTestSupport {
     
     @Test
     public void testCreateGetContainsAndRemoveTags() throws Exception {
-        entity.addTag("foo");
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+            .addInitializer(EntityInitializers.addingTags("foo")));
+        
         entity.addTag(app);
         
         Assert.assertTrue(entity.containsTag("foo"));
@@ -122,5 +124,5 @@ public class EntitiesTest extends BrooklynAppUnitTestSupport {
         
         Assert.assertEquals(entity.getTags(), MutableSet.of(app));
     }
-
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1a294f76/utils/common/src/test/java/brooklyn/util/net/UrlsTest.java
----------------------------------------------------------------------
diff --git a/utils/common/src/test/java/brooklyn/util/net/UrlsTest.java b/utils/common/src/test/java/brooklyn/util/net/UrlsTest.java
index 017ad3d..d0a615c 100644
--- a/utils/common/src/test/java/brooklyn/util/net/UrlsTest.java
+++ b/utils/common/src/test/java/brooklyn/util/net/UrlsTest.java
@@ -43,6 +43,11 @@ public class UrlsTest {
     }
 
     @Test
+    public void testPathEncode() throws Exception {
+        assertEquals(Urls.encode("name_with/%!"), "name_with%2F%25%21");
+    }
+
+    @Test
     public void testIsUrlWithProtocol() {
         Assert.assertTrue(Urls.isUrlWithProtocol("http://localhost/"));
         Assert.assertTrue(Urls.isUrlWithProtocol("protocol:"));


[10/13] git commit: fix intermittent failing test by ensuring we wait for persistence to complete

Posted by he...@apache.org.
fix intermittent failing test by ensuring we wait for persistence to complete


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

Branch: refs/heads/master
Commit: ebfec154ae569ba5a7e9257e7415a928857b933c
Parents: cefcda3
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Jul 18 09:51:15 2014 -0400
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Jul 18 12:26:21 2014 -0400

----------------------------------------------------------------------
 .../BrooklynMementoPersisterInMemorySizeIntegrationTest.java  | 7 +++++++
 1 file changed, 7 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ebfec154/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemorySizeIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemorySizeIntegrationTest.java b/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemorySizeIntegrationTest.java
index 1d82962..d2638cf 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemorySizeIntegrationTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemorySizeIntegrationTest.java
@@ -49,6 +49,10 @@ public class BrooklynMementoPersisterInMemorySizeIntegrationTest extends Brookly
     public void testPersistenceVolumeFast() throws IOException, TimeoutException, InterruptedException {
         doTestPersistenceVolume(50*1000, false);
     }
+    @Test(groups="Integration",invocationCount=20)
+    public void testPersistenceVolumeFastManyTimes() throws IOException, TimeoutException, InterruptedException {
+        doTestPersistenceVolume(50*1000, false);
+    }
     @Test(groups="Integration")
     public void testPersistenceVolumeWaiting() throws IOException, TimeoutException, InterruptedException {
         // by waiting we ensure there aren't extra writes going on
@@ -58,6 +62,7 @@ public class BrooklynMementoPersisterInMemorySizeIntegrationTest extends Brookly
     protected void doTestPersistenceVolume(int bigBlockSize, boolean forceDelay) throws IOException, TimeoutException, InterruptedException {
         if (forceDelay) Time.sleep(Duration.FIVE_SECONDS);
         else recorder.blockUntilDataWrittenExceeds(512, Duration.FIVE_SECONDS);
+        localManagementContext.getRebindManager().waitForPendingComplete(Duration.FIVE_SECONDS);
         
         long out1 = recorder.getBytesOut();
         int filesOut1 = recorder.getCountDataOut();
@@ -68,6 +73,7 @@ public class BrooklynMementoPersisterInMemorySizeIntegrationTest extends Brookly
         ((EntityInternal)app).setAttribute(TestEntity.NAME, "hello world");
         if (forceDelay) Time.sleep(Duration.FIVE_SECONDS);
         else recorder.blockUntilDataWrittenExceeds(out1+10, Duration.FIVE_SECONDS);
+        localManagementContext.getRebindManager().waitForPendingComplete(Duration.FIVE_SECONDS);
         
         long out2 = recorder.getBytesOut();
         Assert.assertTrue(out2-out1>10, "should have written more data");
@@ -80,6 +86,7 @@ public class BrooklynMementoPersisterInMemorySizeIntegrationTest extends Brookly
         ((EntityInternal)entity).setAttribute(TestEntity.NAME, Identifiers.makeRandomId(bigBlockSize));
         if (forceDelay) Time.sleep(Duration.FIVE_SECONDS);
         else recorder.blockUntilDataWrittenExceeds(out2+bigBlockSize, Duration.FIVE_SECONDS);
+        localManagementContext.getRebindManager().waitForPendingComplete(Duration.FIVE_SECONDS);
 
         long out3 = recorder.getBytesOut();
         Assert.assertTrue(out3-out2 > bigBlockSize, "should have written 50k more data, only wrote "+out3+" compared with "+out2);


[08/13] git commit: more tests for BrooklynNode, including test which uses REST API of local instance (and new "SameBrooklynNodeImpl" semi-mock); and adds BrooklynEntityMirror with test

Posted by he...@apache.org.
more tests for BrooklynNode, including test which uses REST API of local instance (and new "SameBrooklynNodeImpl" semi-mock); and adds BrooklynEntityMirror with test


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

Branch: refs/heads/master
Commit: 8569120c909e782b5ca2f90525795822f3e9395b
Parents: 1a294f7
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Jul 17 23:50:20 2014 -0400
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Jul 18 12:26:20 2014 -0400

----------------------------------------------------------------------
 .../brooklyn/config/BrooklynServerConfig.java   |   6 +
 .../config/BrooklynServiceAttributes.java       |   2 +-
 .../brooklynnode/BrooklynEntityMirror.java      |  52 ++++++
 .../brooklynnode/BrooklynEntityMirrorImpl.java  | 169 +++++++++++++++++++
 .../entity/brooklynnode/BrooklynNode.java       |   3 +
 .../entity/brooklynnode/BrooklynNodeImpl.java   |   8 +-
 .../entity/brooklynnode/BrooklynNodeTest.java   |  18 ++
 .../brooklynnode/SameBrooklynNodeImpl.java      |  86 ++++++++++
 .../camp/BrooklynCampPlatformLauncher.java      |   7 +-
 .../entity/basic/VanillaSoftwareYamlTest.java   |   5 +-
 .../brooklynnode/BrooklynNodeRestTest.java      |  92 ++++++++++
 .../launcher/BrooklynWebServerTest.java         |   1 -
 .../launcher/SimpleYamlLauncherForTests.java    |  31 ++++
 .../java/brooklyn/util/collections/Jsonya.java  |   8 +
 14 files changed, 480 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/config/BrooklynServerConfig.java b/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
index fccba64..adc41be 100644
--- a/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
+++ b/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
@@ -22,6 +22,7 @@ import static brooklyn.entity.basic.ConfigKeys.newStringConfigKey;
 import io.brooklyn.camp.CampPlatform;
 
 import java.io.File;
+import java.net.URI;
 import java.util.Map;
 
 import org.apache.commons.io.FileUtils;
@@ -164,4 +165,9 @@ public class BrooklynServerConfig {
         return Maybe.absent("No CAMP Platform is registered with this Brooklyn management context.");
     }
 
+    /** Returns {@link ManagementContext#getManagementNodeUri()}, located in this utility class for convenience. */
+    public static Maybe<URI> getBrooklynWebUri(ManagementContext mgmt) {
+        return mgmt.getManagementNodeUri();
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/core/src/main/java/brooklyn/config/BrooklynServiceAttributes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/config/BrooklynServiceAttributes.java b/core/src/main/java/brooklyn/config/BrooklynServiceAttributes.java
index 2eac234..840bf59 100644
--- a/core/src/main/java/brooklyn/config/BrooklynServiceAttributes.java
+++ b/core/src/main/java/brooklyn/config/BrooklynServiceAttributes.java
@@ -38,7 +38,7 @@ public class BrooklynServiceAttributes {
 	
     /** used to hold the instance of ManagementContext which should be used */
     public static final String BROOKLYN_MANAGEMENT_CONTEXT = ManagementContext.class.getName();
-
+    
     /** poor-man's security, to specify a user to be automatically logged in
      * (e.g. to bypass security, during dev/test); 'admin' is usually a sensible choice.
      * if not specified (the default) username+password is required. 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirror.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirror.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirror.java
new file mode 100644
index 0000000..2773793
--- /dev/null
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirror.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.entity.brooklynnode;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.brooklynnode.BrooklynNode;
+import brooklyn.entity.proxying.ImplementedBy;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.basic.Sensors;
+import brooklyn.util.time.Duration;
+
+/** Provides an entity which can sit in one brooklyn domain and reflect the status of an entity 
+ * via the REST API of another domain */
+@ImplementedBy(BrooklynEntityMirrorImpl.class)
+public interface BrooklynEntityMirror extends Entity {
+
+    // caller must specify this:
+    public static final ConfigKey<String> MIRRORED_ENTITY_URL = ConfigKeys.newStringConfigKey("brooklyn.mirror.entity_url",
+        "URL for the entity in the remote Brooklyn mgmt endpoint");
+    
+    // caller may specify this for reference:
+    public static final ConfigKey<String> MIRRORED_ENTITY_ID = ConfigKeys.newStringConfigKey("brooklyn.mirror.entity_id",
+        "Brooklyn ID of the entity being mirrored");
+    
+    // must be specified if required (could be inherited if parent/config is available at init time, but it's not currently)
+    public static final ConfigKey<String> MANAGEMENT_USER = BrooklynNode.MANAGEMENT_USER;
+    public static final ConfigKey<String> MANAGEMENT_PASSWORD = BrooklynNode.MANAGEMENT_PASSWORD;
+    
+    public static final ConfigKey<Duration> POLL_PERIOD = ConfigKeys.newConfigKey(Duration.class, "brooklyn.mirror.poll_period",
+        "Frequency to poll for client sensors", Duration.FIVE_SECONDS);
+    
+    public static final AttributeSensor<String> MIRROR_STATUS = Sensors.newStringSensor("brooklyn.mirror.monitoring_status");
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirrorImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirrorImpl.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirrorImpl.java
new file mode 100644
index 0000000..8407708
--- /dev/null
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirrorImpl.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.entity.brooklynnode;
+
+import java.net.URI;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.HttpClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.AbstractEntity;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityFunctions;
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.effector.EffectorBody;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.basic.Sensors;
+import brooklyn.event.feed.http.HttpFeed;
+import brooklyn.event.feed.http.HttpPollConfig;
+import brooklyn.util.collections.Jsonya;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.http.HttpTool;
+import brooklyn.util.http.HttpTool.HttpClientBuilder;
+import brooklyn.util.http.HttpToolResponse;
+import brooklyn.util.net.Urls;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.task.Tasks;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.gson.Gson;
+
+public class BrooklynEntityMirrorImpl extends AbstractEntity implements BrooklynEntityMirror {
+
+    private static final Logger log = LoggerFactory.getLogger(BrooklynEntityMirrorImpl.class);
+    
+    private HttpFeed mirror;
+    
+    @Override
+    public void init() {
+        super.init();
+        connectSensors();
+    }
+
+    protected void connectSensors() {
+        Function<HttpToolResponse, Void> mirrorSensors = new Function<HttpToolResponse,Void>() {
+            @SuppressWarnings("rawtypes")
+            @Override
+            public Void apply(HttpToolResponse input) {
+                Map sensors = new Gson().fromJson(input.getContentAsString(), Map.class);
+                for (Object kv: sensors.entrySet())
+                    setAttribute(Sensors.newSensor(Object.class, ""+((Map.Entry)kv).getKey()), ((Map.Entry)kv).getValue());
+                setAttribute(MIRROR_STATUS, "normal");
+                return null;
+            }
+        };
+        
+        String sensorsUri = Urls.mergePaths(
+            Preconditions.checkNotNull(getConfig(MIRRORED_ENTITY_URL), "Required config: "+MIRRORED_ENTITY_URL),
+            "sensors/current-state");
+        
+        mirror = HttpFeed.builder().entity(this)
+            .baseUri(sensorsUri)
+            .credentialsIfNotNull(getConfig(BrooklynNode.MANAGEMENT_USER), getConfig(BrooklynNode.MANAGEMENT_PASSWORD))
+            .period(getConfig(POLL_PERIOD))
+            .poll(HttpPollConfig.forMultiple()
+                .onSuccess(mirrorSensors)
+                .onFailureOrException(EntityFunctions.settingSensorsConstantFunction(this, MutableMap.<AttributeSensor<?>,Object>of(
+                    Attributes.SERVICE_STATE, Lifecycle.ON_FIRE,
+                    MIRROR_STATUS, "error contacting service"
+                    ))) )
+            .build();
+    }
+
+    protected void disconnectSensors() {
+        if (mirror != null) mirror.stop();
+    }
+
+    @Override
+    public void destroy() {
+        disconnectSensors();
+    }
+
+    public static class RemoteEffector<T> extends EffectorBody<T> {
+        public final String remoteEffectorName;
+        public final Function<byte[], T> resultParser;
+        
+        /** creates an effector implementation which POSTs to a remote effector endpoint, optionally converting
+         * the byte[] response (if resultParser is null then null is returned) */
+        public RemoteEffector(String remoteEffectorName, @Nullable Function<byte[],T> resultParser) {
+            this.remoteEffectorName = remoteEffectorName;
+            this.resultParser = resultParser;
+        }
+
+        @Override
+        public T call(ConfigBag parameters) {
+            String baseUri = Preconditions.checkNotNull(entity().getConfig(MIRRORED_ENTITY_URL), "Cannot be invoked without an entity URL");
+            HttpClientBuilder builder = HttpTool.httpClientBuilder()
+                .trustAll()
+                .laxRedirect(true)
+                .uri(baseUri);
+            if (entity().getConfig(MANAGEMENT_USER)!=null)
+                builder.credentials(new UsernamePasswordCredentials(entity().getConfig(MANAGEMENT_USER), entity().getConfig(MANAGEMENT_PASSWORD)));
+            HttpClient client = builder.build();
+            
+            byte[] result = submit(client, URI.create(Urls.mergePaths(baseUri, "effectors", Urls.encode(remoteEffectorName))), parameters.getAllConfig());
+            if (resultParser!=null) return resultParser.apply(result);
+            else return null;
+        }
+
+        @VisibleForTesting
+        public static byte[] submit(HttpClient client, URI uri, Map<String,Object> args) {
+            HttpToolResponse result = null;
+            byte[] content;
+            try {
+                result = HttpTool.httpPost(client, uri, MutableMap.of(com.google.common.net.HttpHeaders.CONTENT_TYPE, "application/json"), 
+                    Jsonya.of(args).toString().getBytes());
+                content = result.getContent();
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                throw new IllegalStateException("Invalid response invoking "+uri+": "+e, e);
+            }
+            Tasks.addTagDynamically(BrooklynTaskTags.tagForStream("http_response", Streams.byteArray(content)));
+            if (!HttpTool.isStatusCodeHealthy(result.getResponseCode())) {
+                log.warn("Invalid response invoking "+uri+": response code "+result.getResponseCode()+"\n"+result+": "+new String(content));
+                throw new IllegalStateException("Invalid response invoking "+uri+": response code "+result.getResponseCode());
+            }
+            return content;
+        }
+
+    }
+
+    public static class StopAndExpungeEffector extends RemoteEffector<Void> {
+        public StopAndExpungeEffector() {
+            super("stop", null);
+        }
+        @Override
+        public Void call(ConfigBag parameters) {
+            super.call(parameters);
+            Entities.unmanage(entity());
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java
index e61fd25..75523e3 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java
@@ -166,6 +166,7 @@ public interface BrooklynNode extends SoftwareProcess, UsesJava {
     public static final ConfigKey<String> BROOKLYN_CATALOG_CONTENTS = ConfigKeys.newStringConfigKey(
             "brooklynnode.brooklyncatalog.contents", "Contents for the brooklyn catalog.xml file (to upload to ~/.brooklyn/catalog.xml", null);
 
+    @SuppressWarnings({ "rawtypes", "unchecked" })
     @SetFromFlag("enabledHttpProtocols")
     public static final BasicAttributeSensorAndConfigKey<List<String>> ENABLED_HTTP_PROTOCOLS = new BasicAttributeSensorAndConfigKey(
             List.class, "brooklynnode.webconsole.enabledHttpProtocols", "List of enabled protocols (e.g. http, https)", ImmutableList.of("http"));
@@ -186,10 +187,12 @@ public interface BrooklynNode extends SoftwareProcess, UsesJava {
     public static final BasicAttributeSensorAndConfigKey<String> WEB_CONSOLE_BIND_ADDRESS = new BasicAttributeSensorAndConfigKey<String>(
             String.class, "brooklynnode.webconsole.bindAddress", "Specifies the IP address of the NIC to bind the Brooklyn Management Console to", null);
 
+    @SuppressWarnings({ "rawtypes", "unchecked" })
     @SetFromFlag("classpath")
     public static final BasicAttributeSensorAndConfigKey<List<String>> CLASSPATH = new BasicAttributeSensorAndConfigKey(
             List.class, "brooklynnode.classpath", "classpath to use, as list of URL entries", Lists.newArrayList());
 
+    @SuppressWarnings({ "rawtypes", "unchecked" })
     @SetFromFlag("portMapper")
     public static final ConfigKey<Function<? super Integer, ? extends Integer>> PORT_MAPPER = (ConfigKey) ConfigKeys.newConfigKey(Function.class,
             "brooklynnode.webconsole.portMapper", "Function for mapping private to public ports, for use in inferring the brooklyn URI", Functions.<Integer>identity());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
index 983cce5..5c29980 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
@@ -75,7 +75,7 @@ public class BrooklynNodeImpl extends SoftwareProcessImpl implements BrooklynNod
     }
     
     @Override
-    public Class getDriverInterface() {
+    public Class<?> getDriverInterface() {
         return BrooklynNodeDriver.class;
     }
 
@@ -90,6 +90,7 @@ public class BrooklynNodeImpl extends SoftwareProcessImpl implements BrooklynNod
         
         // TODO support YAML parsing
         // TODO define a new type YamlMap for the config key which supports coercing from string and from map
+        @SuppressWarnings("unchecked")
         public static Map<String,Object> asMap(ConfigBag parameters, ConfigKey<?> key) {
             Object v = parameters.getStringKey(key.getName());
             if (v==null || (v instanceof String && Strings.isBlank((String)v)))
@@ -121,8 +122,9 @@ public class BrooklynNodeImpl extends SoftwareProcessImpl implements BrooklynNod
                 throw new IllegalArgumentException("Must supply plan or url");
             
             Map<String, Object> config = asMap(parameters, BLUEPRINT_CONFIG);
+            
             if (planRaw==null) {
-                planRaw = Jsonya.at("services").list().put("serviceType", url).put(config).getRootMap();
+                planRaw = Jsonya.at("services").list().put("serviceType", url).putIfNotNull("brooklyn.config", config).getRootMap();
             } else { 
                 if (config!=null)
                     throw new IllegalArgumentException("Cannot supply plan with config");
@@ -130,7 +132,7 @@ public class BrooklynNodeImpl extends SoftwareProcessImpl implements BrooklynNod
             
             // planRaw might be a yaml string, or a map; if a map, convert to string
             if (planRaw instanceof Map)
-                planRaw = Jsonya.of((Map)planRaw).toString();
+                planRaw = Jsonya.of((Map<?,?>)planRaw).toString();
             if (!(planRaw instanceof String))
                 throw new IllegalArgumentException("Invalid "+JavaClassNames.simpleClassName(planRaw)+" value for CAMP plan: "+planRaw);
             

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeTest.java b/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeTest.java
index 8f7d561..9752ec1 100644
--- a/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeTest.java
+++ b/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeTest.java
@@ -22,16 +22,21 @@ import static org.testng.Assert.assertTrue;
 
 import java.util.List;
 
+import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.drivers.downloads.DownloadResolver;
+import brooklyn.entity.proxying.EntitySpec;
 import brooklyn.event.feed.ConfigToAttributes;
+import brooklyn.location.Location;
 import brooklyn.location.basic.SshMachineLocation;
 import brooklyn.test.entity.TestApplication;
 import brooklyn.util.collections.MutableMap;
+import brooklyn.util.collections.MutableSet;
 
 public class BrooklynNodeTest {
 
@@ -79,4 +84,17 @@ public class BrooklynNodeTest {
         System.out.println("urls="+urls);
         assertTrue(urls.contains(expectedUrl), "urls="+urls);
     }
+    
+    @Test
+    public void testCanStartSameNode() throws Exception {
+        // not very interesting as done not have REST when run in this project
+        // but test BrooklynNodeRestTest in downstream project does
+        BrooklynNode bn = app.createAndManageChild(EntitySpec.create(BrooklynNode.class, SameBrooklynNodeImpl.class));
+        bn.start(MutableSet.<Location>of());
+        
+        Assert.assertEquals(bn.getAttribute(Attributes.SERVICE_UP), (Boolean)true);
+        // no URI
+        Assert.assertNull(bn.getAttribute(BrooklynNode.WEB_CONSOLE_URI));
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/software/base/src/test/java/brooklyn/entity/brooklynnode/SameBrooklynNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/brooklynnode/SameBrooklynNodeImpl.java b/software/base/src/test/java/brooklyn/entity/brooklynnode/SameBrooklynNodeImpl.java
new file mode 100644
index 0000000..76e918c
--- /dev/null
+++ b/software/base/src/test/java/brooklyn/entity/brooklynnode/SameBrooklynNodeImpl.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.entity.brooklynnode;
+
+import java.net.URI;
+import java.util.Collection;
+
+import brooklyn.entity.basic.AbstractEntity;
+import brooklyn.entity.brooklynnode.BrooklynNodeImpl.DeployBlueprintEffectorBody;
+import brooklyn.event.feed.http.HttpFeed;
+import brooklyn.event.feed.http.HttpPollConfig;
+import brooklyn.event.feed.http.HttpValueFunctions;
+import brooklyn.location.Location;
+
+/** Implementation of BrooklynNode which just presents the node where this is running, for convenience;
+ * 
+ *  start/stop/restart have no effect; 
+ *  sensors are connected;
+ *  deploy blueprint assumes that a REST endpoint is available */
+public class SameBrooklynNodeImpl extends AbstractEntity implements BrooklynNode {
+    
+    private HttpFeed httpFeed;
+
+    @Override
+    public void start(Collection<? extends Location> locations) {
+        connectSensors();
+    }
+
+    @Override
+    public void stop() {
+        disconnectSensors();
+    }
+
+    @Override
+    public void restart() {
+        return;
+    }
+
+    
+    @Override
+    public void init() {
+        super.init();
+        getMutableEntityType().addEffector(DeployBlueprintEffectorBody.DEPLOY_BLUEPRINT);
+    }
+    
+    protected void connectSensors() {
+        URI webConsoleUri = getManagementContext().getManagementNodeUri().orNull();
+        setAttribute(WEB_CONSOLE_URI, webConsoleUri);
+
+        if (webConsoleUri != null) {
+            httpFeed = HttpFeed.builder()
+                    .entity(this)
+                    .period(200)
+                    .baseUri(webConsoleUri)
+                    .credentialsIfNotNull(getConfig(MANAGEMENT_USER), getConfig(MANAGEMENT_PASSWORD))
+                    .poll(new HttpPollConfig<Boolean>(SERVICE_UP)
+                            .onSuccess(HttpValueFunctions.responseCodeEquals(200))
+                            .setOnFailureOrException(false))
+                    .build();
+
+        } else {
+            setAttribute(SERVICE_UP, true);
+        }
+    }
+    
+    protected void disconnectSensors() {
+        if (httpFeed != null) httpFeed.stop();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/usage/launcher/src/main/java/brooklyn/launcher/camp/BrooklynCampPlatformLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/brooklyn/launcher/camp/BrooklynCampPlatformLauncher.java b/usage/launcher/src/main/java/brooklyn/launcher/camp/BrooklynCampPlatformLauncher.java
index c05c94f..c8e7df4 100644
--- a/usage/launcher/src/main/java/brooklyn/launcher/camp/BrooklynCampPlatformLauncher.java
+++ b/usage/launcher/src/main/java/brooklyn/launcher/camp/BrooklynCampPlatformLauncher.java
@@ -24,6 +24,7 @@ import io.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherAbstract;
 import io.brooklyn.camp.spi.PlatformRootSummary;
 import brooklyn.entity.basic.BrooklynShutdownHooks;
 import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.management.ManagementContext;
 import brooklyn.management.internal.LocalManagementContext;
 
 import com.google.common.annotations.Beta;
@@ -39,7 +40,7 @@ public class BrooklynCampPlatformLauncher extends BrooklynCampPlatformLauncherAb
     public BrooklynCampPlatformLauncher launch() {
         assert platform == null;
 
-        mgmt = new LocalManagementContext();
+        mgmt = newManagementContext();
         
         // We created the management context, so we are responsible for terminating it
         BrooklynShutdownHooks.invokeTerminateOnShutdown(mgmt);
@@ -54,6 +55,10 @@ public class BrooklynCampPlatformLauncher extends BrooklynCampPlatformLauncherAb
         return this;
     }
     
+    protected ManagementContext newManagementContext() {
+        return new LocalManagementContext();
+    }
+
     public static void main(String[] args) {
         new BrooklynCampPlatformLauncher().launch();
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/usage/launcher/src/test/java/brooklyn/entity/basic/VanillaSoftwareYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/entity/basic/VanillaSoftwareYamlTest.java b/usage/launcher/src/test/java/brooklyn/entity/basic/VanillaSoftwareYamlTest.java
index 5ef622e..8fbca64 100644
--- a/usage/launcher/src/test/java/brooklyn/entity/basic/VanillaSoftwareYamlTest.java
+++ b/usage/launcher/src/test/java/brooklyn/entity/basic/VanillaSoftwareYamlTest.java
@@ -26,6 +26,7 @@ import org.testng.annotations.Test;
 import brooklyn.entity.Application;
 import brooklyn.entity.Entity;
 import brooklyn.entity.trait.Startable;
+import brooklyn.launcher.SimpleYamlLauncherForTests;
 import brooklyn.launcher.camp.SimpleYamlLauncher;
 import brooklyn.test.Asserts;
 import brooklyn.util.ResourceUtils;
@@ -40,7 +41,7 @@ public class VanillaSoftwareYamlTest {
     
     @Test(groups="Integration")
     public void testVanillaSoftwareYaml() {
-        SimpleYamlLauncher l = new SimpleYamlLauncher();
+        SimpleYamlLauncher l = new SimpleYamlLauncherForTests();
         try {
             Application app = l.launchAppYaml("vanilla-software-blueprint.yaml");
             log.info("started "+app);
@@ -76,7 +77,7 @@ public class VanillaSoftwareYamlTest {
     /** yaml variant of VanillaSoftwareProcessAndChildrenIntegrationTest */
     @Test(groups="Integration")
     public void testVanillaSoftwareYamlWithChildStartedAfter() {
-        SimpleYamlLauncher l = new SimpleYamlLauncher();
+        SimpleYamlLauncher l = new SimpleYamlLauncherForTests();
         try {
             Application app = l.launchAppYaml("vanilla-software-with-child-blueprint.yaml");
             log.info("started "+app);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/usage/launcher/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java b/usage/launcher/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java
new file mode 100644
index 0000000..2cd97f0
--- /dev/null
+++ b/usage/launcher/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java
@@ -0,0 +1,92 @@
+package brooklyn.entity.brooklynnode;
+
+import java.net.URI;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.Application;
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.event.basic.BasicConfigKey;
+import brooklyn.launcher.SimpleYamlLauncherForTests;
+import brooklyn.launcher.camp.SimpleYamlLauncher;
+import brooklyn.location.Location;
+import brooklyn.management.Task;
+import brooklyn.test.EntityTestUtils;
+import brooklyn.test.entity.TestApplication;
+import brooklyn.test.entity.TestEntity;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.collections.MutableSet;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.net.Urls;
+import brooklyn.util.time.Duration;
+
+import com.google.common.collect.Iterables;
+
+/** REST-accessible extension of {@link BrooklynNodeTest} */
+public class BrooklynNodeRestTest {
+
+    private static final Logger log = LoggerFactory.getLogger(BrooklynNodeRestTest.class);
+    
+    // takes a while when run on its own, because initializing war and making some requests;
+    // but there are no waits (beyond 10ms), the delay is all classloading;
+    // and this tests a lot of things, REST API, Brooklyn Node, yaml deployment,
+    // so feels worth it to have as a unit test
+    @Test
+    public void testBrooklynNodeRestDeployAndMirror() {
+        SimpleYamlLauncher l = new SimpleYamlLauncherForTests();
+        try {
+            TestApplication app = ApplicationBuilder.newManagedApp(TestApplication.class, l.getManagementContext());
+
+            BrooklynNode bn = app.createAndManageChild(EntitySpec.create(BrooklynNode.class, SameBrooklynNodeImpl.class));
+            bn.start(MutableSet.<Location>of());
+            
+            URI uri = bn.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
+            Assert.assertNotNull(uri);
+            EntityTestUtils.assertAttributeEqualsEventually(bn, Attributes.SERVICE_UP, true);
+            log.info("Created BrooklynNode: "+bn);
+
+            // deploy
+            Task<?> t = bn.invoke(BrooklynNode.DEPLOY_BLUEPRINT, ConfigBag.newInstance()
+                .configure(BrooklynNode.DeployBlueprintEffector.BLUEPRINT_TYPE, TestApplication.class.getName())
+                .configure(BrooklynNode.DeployBlueprintEffector.BLUEPRINT_CONFIG, MutableMap.<String,Object>of("x", 1, "y", "Y"))
+                .getAllConfig());
+            log.info("Deployment result: "+t.getUnchecked());
+            
+            MutableSet<Application> apps = MutableSet.copyOf( l.getManagementContext().getApplications() );
+            Assert.assertEquals(apps.size(), 2);
+            apps.remove(app);
+            
+            Application newApp = Iterables.getOnlyElement(apps);
+            Entities.dumpInfo(newApp);
+            
+            Assert.assertEquals(newApp.getConfig(new BasicConfigKey<Integer>(Integer.class, "x")), (Integer)1);
+            
+            // check mirror
+            String newAppId = newApp.getId();
+            BrooklynEntityMirror mirror = app.createAndManageChild(EntitySpec.create(BrooklynEntityMirror.class)
+                .configure(BrooklynEntityMirror.MIRRORED_ENTITY_URL, 
+                    Urls.mergePaths(uri.toString(), "/v1/applications/"+newAppId+"/entities/"+newAppId))
+                .configure(BrooklynEntityMirror.MIRRORED_ENTITY_ID, newAppId)
+                .configure(BrooklynEntityMirror.POLL_PERIOD, Duration.millis(10)));
+            
+            Entities.dumpInfo(mirror);
+            
+            EntityTestUtils.assertAttributeEqualsEventually(mirror, Attributes.SERVICE_UP, true);
+            
+            ((EntityInternal)newApp).setAttribute(TestEntity.NAME, "foo");
+            EntityTestUtils.assertAttributeEqualsEventually(mirror, TestEntity.NAME, "foo");
+            log.info("Mirror successfully validated");
+            
+        } finally {
+            l.destroyAll();
+        }
+        log.info("DONE");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/usage/launcher/src/test/java/brooklyn/launcher/BrooklynWebServerTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynWebServerTest.java b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynWebServerTest.java
index db9b986..ac37889 100644
--- a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynWebServerTest.java
+++ b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynWebServerTest.java
@@ -32,7 +32,6 @@ import org.apache.http.conn.ssl.SSLSocketFactory;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/usage/launcher/src/test/java/brooklyn/launcher/SimpleYamlLauncherForTests.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/launcher/SimpleYamlLauncherForTests.java b/usage/launcher/src/test/java/brooklyn/launcher/SimpleYamlLauncherForTests.java
new file mode 100644
index 0000000..9ab8131
--- /dev/null
+++ b/usage/launcher/src/test/java/brooklyn/launcher/SimpleYamlLauncherForTests.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.launcher;
+
+import brooklyn.launcher.camp.SimpleYamlLauncher;
+import brooklyn.management.ManagementContext;
+import brooklyn.test.entity.LocalManagementContextForTests;
+
+public class SimpleYamlLauncherForTests extends SimpleYamlLauncher {
+
+    protected ManagementContext newManagementContext() {
+        return new LocalManagementContextForTests();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8569120c/utils/common/src/main/java/brooklyn/util/collections/Jsonya.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/collections/Jsonya.java b/utils/common/src/main/java/brooklyn/util/collections/Jsonya.java
index 2060dac..383b926 100644
--- a/utils/common/src/main/java/brooklyn/util/collections/Jsonya.java
+++ b/utils/common/src/main/java/brooklyn/util/collections/Jsonya.java
@@ -264,6 +264,14 @@ public class Jsonya {
             return this;
         }
         
+        public Navigator<T> putIfNotNull(Object k1, Object v1) {
+            if (v1!=null) {
+                map();
+                putInternal((Map)focus, k1, v1);
+            }
+            return this;
+        }
+        
         protected void putInternal(Map target, Object k1, Object v1, Object ...kvOthers) {
             assert (kvOthers.length % 2) == 0 : "even number of arguments required for put";
             target.put(translateKey(k1), translate(v1));


[06/13] git commit: remove dead code and tidy warnings in camp code

Posted by he...@apache.org.
remove dead code and tidy warnings in camp code


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

Branch: refs/heads/master
Commit: 78da216ceb8897ec6378bd11a5ee5ea7e0c1a28a
Parents: a4f7a4c
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Jul 17 23:32:55 2014 -0400
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Jul 18 09:53:53 2014 -0400

----------------------------------------------------------------------
 .../brooklyn/spi/creation/BrooklynComponentTemplateResolver.java    | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/78da216c/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index 155342a..8a0f459 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -241,6 +241,7 @@ public class BrooklynComponentTemplateResolver {
         } else {
             // If this is a concrete class, particularly for an Application class, we want the proxy
             // to expose all interfaces it implements.
+            @SuppressWarnings("rawtypes")
             Class interfaceclazz = (Application.class.isAssignableFrom(type)) ? Application.class : Entity.class;
             List<Class<?>> additionalInterfaceClazzes = Reflections.getAllInterfaces(type);
             spec = EntitySpec.create(interfaceclazz).impl(type).additionalInterfaces(additionalInterfaceClazzes);


[13/13] git commit: This closes #78

Posted by he...@apache.org.
This closes #78


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

Branch: refs/heads/master
Commit: 4fa68908bafc7fa7063cf56fdac7420d561e93dc
Parents: 562325a ebfec15
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Jul 18 12:33:02 2014 -0400
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Jul 18 12:33:02 2014 -0400

----------------------------------------------------------------------
 camp/camp-base/pom.xml                          |   5 -
 .../java/io/brooklyn/camp/spi/pdp/Artifact.java |   3 +-
 .../camp/spi/pdp/ArtifactRequirement.java       |   3 +-
 .../java/io/brooklyn/camp/spi/pdp/Service.java  |   3 +-
 .../camp/spi/pdp/ServiceCharacteristic.java     |   3 +-
 .../brooklyn/camp/spi/resolve/PdpProcessor.java |   2 +-
 .../main/java/io/brooklyn/util/yaml/Yamls.java  |  89 +---------
 .../pdp/DeploymentPlanToyInterpreterTest.java   |   2 +-
 .../brooklyn/config/BrooklynServerConfig.java   |   6 +
 .../config/BrooklynServiceAttributes.java       |   2 +-
 .../entity/basic/EntityInitializers.java        |  30 ++++
 .../java/brooklyn/util/flags/TypeCoercions.java |  85 ++++++----
 .../brooklyn/entity/basic/EntitiesTest.java     |   6 +-
 ...ntoPersisterInMemorySizeIntegrationTest.java |   7 +
 .../util/internal/TypeCoercionsTest.java        |  72 ++++++--
 .../brooklynnode/BrooklynEntityMirror.java      |  52 ++++++
 .../brooklynnode/BrooklynEntityMirrorImpl.java  | 169 +++++++++++++++++++
 .../entity/brooklynnode/BrooklynNode.java       |   3 +
 .../entity/brooklynnode/BrooklynNodeImpl.java   |   8 +-
 .../BrooklynNodeIntegrationTest.java            |  31 +++-
 .../entity/brooklynnode/BrooklynNodeTest.java   |  22 ++-
 .../brooklynnode/SameBrooklynNodeImpl.java      |  86 ++++++++++
 .../BrooklynComponentTemplateResolver.java      |   1 +
 usage/launcher/pom.xml                          |   7 +
 .../camp/BrooklynCampPlatformLauncher.java      |   7 +-
 .../entity/basic/VanillaSoftwareYamlTest.java   |   5 +-
 .../brooklynnode/BrooklynNodeRestTest.java      | 145 ++++++++++++++++
 .../launcher/BrooklynWebServerTest.java         |   1 -
 .../launcher/SimpleYamlLauncherForTests.java    |  31 ++++
 utils/common/pom.xml                            |   5 +
 .../java/brooklyn/util/collections/Jsonya.java  |   8 +
 .../src/main/java/brooklyn/util/yaml/Yamls.java | 115 +++++++++++++
 .../test/java/brooklyn/util/net/UrlsTest.java   |   5 +
 33 files changed, 857 insertions(+), 162 deletions(-)
----------------------------------------------------------------------



[12/13] git commit: This closes #72

Posted by he...@apache.org.
This closes #72


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

Branch: refs/heads/master
Commit: 562325a66b8a34a9712c4b38ae80114ce0744b66
Parents: 671fb17 261afa8
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Jul 18 12:32:46 2014 -0400
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Jul 18 12:32:46 2014 -0400

----------------------------------------------------------------------

----------------------------------------------------------------------



[02/13] git commit: Reduce memory usage in BrooklynGarbageCollector

Posted by he...@apache.org.
Reduce memory usage in BrooklynGarbageCollector


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

Branch: refs/heads/master
Commit: fe0d9d3f8747b74c42fbe788fc69e11ab3f5be39
Parents: 35363d6
Author: Aled Sage <al...@gmail.com>
Authored: Wed Jul 16 15:40:47 2014 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Jul 17 11:28:44 2014 +0100

----------------------------------------------------------------------
 .../internal/BrooklynGarbageCollector.java      | 32 ++++++++++++++++----
 1 file changed, 26 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fe0d9d3f/core/src/main/java/brooklyn/management/internal/BrooklynGarbageCollector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/BrooklynGarbageCollector.java b/core/src/main/java/brooklyn/management/internal/BrooklynGarbageCollector.java
index a13458c..870b4fe 100644
--- a/core/src/main/java/brooklyn/management/internal/BrooklynGarbageCollector.java
+++ b/core/src/main/java/brooklyn/management/internal/BrooklynGarbageCollector.java
@@ -199,9 +199,25 @@ public class BrooklynGarbageCollector {
 
     /**
      * Deletes old tasks. The age/number of tasks to keep is controlled by fields like 
-     * {@link #maxTasksPerTag} and {@link #maxTaskAge}. 
+     * {@link #maxTasksPerTag} and {@link #maxTaskAge}.
      */
     private void gcTasks() {
+        // TODO Must be careful with memory usage here : saw an OOME when copying the tasks set to sort it.
+        // (This happened when there were a *lot* of tasks (e.g. 100,000s) due to a crazy trigger for  
+        // effector calls.)
+        // 
+        // At that point we have potentially three copies of the list in-memory:
+        //  - original in ExecutionManager
+        //  - copy returned by getTasksWithTag(tag)
+        //  - copy for sortedTasks (but at least now that only contains done tasks)
+        // 
+        // It's hard to avoid fewer copies: getTasksWithTag(tag) should continue to return copy rather than 
+        // mutable underlying list; and if we're going to sort then we need to take a copy.
+        // 
+        // An option is for getTasksWithTag(tag) to return an ArrayList rather than a LinkedHashSet. That
+        // is a far more memory efficient data structure (e.g. 4 bytes overhead per object rather than 
+        // 32 bytes overhead per object for HashSet).
+        
         if (!running) return;
         
         Set<Object> taskTags = executionManager.getTaskTags();
@@ -216,30 +232,34 @@ public class BrooklynGarbageCollector {
             // For each tag, we find the tasks with that tag; we ignore sub-tasks
             // as those will be automatically GC'ed by the parent task at the appropriate point.
             Set<Task<?>> tasksWithTag = executionManager.getTasksWithTag(tag);
+
             Iterable<Task<?>> topTasksWithTag = Iterables.filter(tasksWithTag, new Predicate<Task<?>>() {
                     @Override public boolean apply(Task<?> input) {
-                        return input != null && !input.getTags().contains(ManagementContextInternal.SUB_TASK_TAG);
+                        return input != null && input.isDone() && !input.getTags().contains(ManagementContextInternal.SUB_TASK_TAG);
                     }});
             
             int numTasksToDelete = (Iterables.size(topTasksWithTag) - maxTasksPerTag);
             if (numTasksToDelete > 0 || maxTaskAge > 0) {
                 List<Task<?>> sortedTasks = Lists.newArrayList(topTasksWithTag);
+                topTasksWithTag = null;
+                tasksWithTag = null;
+                
                 Collections.sort(sortedTasks, new Comparator<Task<?>>() {
                     @Override public int compare(Task<?> t1, Task<?> t2) {
-                        long end1 = t1.isDone() ? t1.getEndTimeUtc() : Long.MAX_VALUE;
-                        long end2 = t2.isDone() ? t2.getEndTimeUtc() : Long.MAX_VALUE;
+                        // know that tasks are all done; filtered those above
+                        long end1 = t1.getEndTimeUtc();
+                        long end2 = t2.getEndTimeUtc();
                         return (end1 < end2) ? -1 : ((end1 == end2) ? 0 : 1);
                     }
                 });
                 if (numTasksToDelete > 0) {
                     for (Task<?> taskToDelete : sortedTasks.subList(0, numTasksToDelete)) {
-                        if (!taskToDelete.isDone()) break;
                         executionManager.deleteTask(taskToDelete);
                     }
                 }
                 if (maxTaskAge > 0) {
                     for (Task<?> taskContender : sortedTasks.subList((numTasksToDelete > 0 ? numTasksToDelete : 0), sortedTasks.size())) {
-                        if (taskContender.isDone() && (System.currentTimeMillis() - taskContender.getEndTimeUtc() > maxTaskAge)) {
+                        if (System.currentTimeMillis() - taskContender.getEndTimeUtc() > maxTaskAge) {
                             executionManager.deleteTask(taskContender);
                         } else {
                             break; // all subsequent tasks will be newer; stop looking